In a worker thread, I iterate over a list of items that need updating. In the main thread, the user often calls a function to add elements to that list. I’m using a mutex to prevent the list from being modified while it’s being iterated. Generally, the pseudocode looks like this:
// called by the client to add an element.
void AddElementToList(element)
{
// lock mutex
// add element to the list
// unlock mutex
}
// this function is run by the worker thread
void WorkerThreadUpdate()
{
// lock mutex
// for each element in the list
// update the element
// mark the element for deletion if finished
// for every finished element
// remove element from list
// unlock mutex
}
The problem comes when updating the element. The element’s update function can potentially take a long time, up to about one second in some cases. When this occurs, the user’s next call to AddElementToList blocks, causing their application to freeze until all the element have been updated. The AddElementToList function is called often enough for this to be a very noticable problem.
So I’m looking for a better method. I want to update the elements while protecting the list but keep the user’s main thread responsive. I’m sure this is pretty much multithreading 101, but I can’t come up with the correct terms to find examples of what I’m looking for.
I was thinking of a solution that uses two lists, a “request” list and a “work” list.
The user calls AddElementToList which adds the element to the request list. In the worker thread, after the work list has been iterated, it goes through the request list and adds its elements to the work list for the next frame. It only locks when it actually modifies the lists.
// called by the client to add an element.
void AddElementToList(element)
{
// lock mutex
// add element to the request list
// unlock mutex
}
// this function is run by the worker thread
void WorkerThreadUpdate()
{
// for each element in the work list
// update the element
// mark the element for deletion if finished
// for every finished element
// lock mutex
// remove element from work list
// unlock mutex
// lock mutex
// for every element in the request list
// add element to the work list
// clear the request list
// unlock mutex
}
I think this should work, but I’m not entirely sure. Is this an acceptable way? Are there better methods for handling this?
Your plan to queue up the additions should work. Whether it’s acceptable or not depends on whether it’s acceptable to wait for queue additions until the thread does its next ‘update’ pass.
Is it even necessary to lock the list during the update? What else is accessing this list, and when? Can you lock the list, make a copy vector/list of the references, unlock the list and then run the update on each of the the copy refs one-by-one?