在工作线程中,我迭代需要更新的项目列表。在主线程中,用户经常调用一个函数来向该列表添加元素。我正在使用互斥锁来防止列表在迭代时被修改。通常,伪代码如下所示:
// 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
}
更新元素时出现问题。元素的更新功能可能需要很长时间,在某些情况下最多可达一秒钟。发生这种情况时,用户下次调用AddElementToList会阻止其应用程序冻结,直到所有元素都已更新。经常调用AddElementToList函数,这是一个非常明显的问题。
所以我正在寻找一种更好的方法。我想在保护列表的同时更新元素,但要保持用户的主线程响应。我敢肯定这是多线程101,但是我无法找到正确的术语来找到我正在寻找的例子。
我在想一个使用两个列表的解决方案,一个“请求”列表和一个“工作”列表。
用户调用AddElementToList,将元素添加到请求列表中。在工作线程中,在迭代工作列表之后,它将遍历请求列表并将其元素添加到下一帧的工作列表中。它仅在实际修改列表时锁定。
// 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
}
我认为这应该有用,但我不完全确定。这是一种可接受的方式吗?有没有更好的方法来处理这个?
答案 0 :(得分:1)
您排队添加的计划应该有效。它是否可接受取决于在线程执行下一次“更新”传递之前等待队列添加是否可接受。
甚至有必要在更新期间锁定列表吗?还有什么是访问此列表,何时?你能锁定列表,制作复制向量/引用列表,解锁列表然后逐个运行每个复制引用的更新吗?
答案 1 :(得分:0)
让我先问一下:
我们假设:
如果是这种情况,我建议以某种方式与此类似:
void AddElementToList(element) {
mutex_lock("push");
list.push_back(element);
mutex_release("push");
}
void WorkerThreadUpdate() {
AddElementToList(specialElement);
/*
at this point we are guaranteed that other threads,
which can only add to the list,
will not affect us, because we have a "guard" between us and them
*/
iterator del=NULL;
iterator i=list.begin();
for(; *i!=specialElement; ++i) {
if (del) {
del->remove();
del=NULL;
}
i->update();
if (some_condition)
del=i;
}
//*i == specialElement now
/*
We are now operating on the guard, so we have to be careful.
"specialElement" could still be the last element in the list.
*/
mutex_lock("push");
i->remove();
mutex_release("push");
}
我不确定标准STL是否是线程安全的,如果你可以使用它们的列表实现。阅读他们的规格。如果不是 - 只需实现自己的列表容器。
答案 2 :(得分:0)
是否有其他线程会尝试访问该元素 你正在更新。如果没有,您可以释放列表中的锁定 启动更新,并在您想要继续时重新获取 迭代(或删除对象)。有点像:
void
WorkerThreadUpdate()
{
// lock mutex
ListType::iterator current = list.begin();
while ( current != list.end() ) {
ListType::value_type& obj = *current;
// unlock mutex
// do modifications...
// lock mutex
if ( needsDeleting ) {
current = list.erase( current );
} else {
++ current;
}
}
// unlock mutex
}
重要的是你必须在实际上持有锁 访问迭代器(至少使用任何标准容器)。
当然,为了以防万一,你会想要使用某种范围的锁。
(虽然这可能没有必要;我认为所有的代码都在
锁定的区域应该是没有扔的。)我没试过,但我想你
如果你有C ++ 11,可以使用std::unique_lock
。