如何在迭代列表时最小化互斥锁,同时能够添加来自另一个线程的元素
How to minimize mutex locks when iterating a list while being able to add elements from another thread?
在工作线程中,我迭代需要更新的项目列表。在主线程中,用户经常调用一个函数来向该列表中添加元素。我使用互斥锁来防止列表在迭代时被修改。一般来说,伪代码看起来像这样:
// 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
}
我认为这应该有效,但我不完全确定。这是可以接受的方式吗?有更好的方法来处理这个问题吗?
您对添加内容进行排队的计划应该会奏效。它是否可接受取决于等待队列添加直到线程进行下一次"更新"过程是否可接受
在更新过程中是否有必要锁定列表?还有什么正在访问此列表,何时访问?你们能锁定列表,制作一个引用的复制向量/列表,解锁列表,然后逐个对每个复制引用运行更新吗?
让我先问一下:
- 您的实现中的列表实际上是什么?一个动态数组?链接列表?一些哈希表?--如果它是一个列表,对一个元素的操作应该只影响下一个、前一个元素,可能还会影响头部和尾部
- 如何将元素添加到列表中?总是在最后?或者插入可以发生在中间的某个地方
让我们假设:
- 它是一个单向或双向的链表,有指向其头部和尾部的附加变量
- 只能在末尾添加元素
如果是这样的话,我建议做类似于这样的事情:
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是否是线程安全的,以及您是否可以使用它们的列表实现。阅读他们的规格。如果没有,只需实现您自己的列表容器。
是否有其他线程尝试访问该元素你正在更新。如果没有,您可以在启动更新,并在想要继续时重新获取迭代(或删除对象)。大致如下:
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,则可以使用CCD_ 1。
IMHO,虽然这是有效的,但它不是特别优雅。我可能会将列表完全保留在工作端,并使用消息队列传递要插入的对象,工作线程从消息队列,以便获取要插入的对象。
- 如何将元素添加到数组的线程安全函数?
- 学习多线程C++:添加线程不会使执行速度更快,即使它看起来应该
- 将项目添加到队列时运行线程
- 为什么添加延迟会提高此多线程环境中的数据吞吐量?
- 如何在Qt中合并/追加/添加两个用于线程的模型?
- 将元素添加到 std::list 在多线程中,无需 C++ 互斥锁
- 在多线程中添加到向量
- 将类Cotaining std ::线程添加到向量
- 如何将元素添加到从另一个线程绑定到 XAML 的 IVector
- 将元素从C 11线程安全地添加到向量中
- 在qabstractItemmodel :: data()const中,如何添加一些线程安全数据
- 以后是否可以将线程添加到 omp 中以进行循环
- 为什么添加函数在C 11线程中无效
- C++:尝试将新节点添加到链表会产生"线程 1:EXC_BAD_ACCESS(代码 = 1,地址 = 0x0)"错误
- 如何仅在使用 c++ 中的 boost 完成所有任务时才向线程池添加新任务
- 为什么队列的所有元素都是一样的,元素被添加到 posix 线程中的队列中
- C++ 用于添加操作的线程安全
- C++:如何在循环中添加线程但不"pause"循环?
- 多个线程同时在unordered_map中添加值会使它崩溃
- 向简单日志记录函数添加线程安全性