[英]How to minimize the mutex locking for an object when only 1 thread mostly uses that object and the other thread(s) use it rarely?
[英]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
}
我认为这应该可行,但是我不确定。 这是可以接受的方式吗? 有更好的方法来解决这个问题吗?
您计划添加的队列应该可以工作。 是否可接受取决于等待队列添加直到线程执行下一次“更新”传递是否可接受。
在更新过程中是否甚至需要锁定列表? 还有什么访问此列表的时间? 您可以锁定列表,创建参考的副本矢量/列表,解锁列表,然后对每个副本ref一对一运行更新吗?
让我先问这些:
假设:
如果是这种情况,我建议您这样做类似于:
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,我认为您可以使用std::unique_lock
。
恕我直言,虽然这可行,但并不特别优雅。 我可能会将列表完全保留在工作程序端,并使用消息队列传递要插入的对象,而工作线程会不时从消息队列中读取消息,以便插入对象。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.