【发布时间】:2019-10-09 01:37:25
【问题描述】:
我有一个列表,我希望不同的线程从中获取元素。为了避免在列表为空时锁定保护列表的互斥锁,我在锁定之前检查了empty()。
如果对 list::empty() 的调用在 100% 的情况下都不是正确的,那也没关系。我只想避免崩溃或中断并发的list::push() 和list::pop() 调用。
我是否可以安全地假设 VC++ 和 Gnu GCC 有时只会出错 empty() 并且不会更糟?
if(list.empty() == false){ // unprotected by mutex, okay if incorrect sometimes
mutex.lock();
if(list.empty() == false){ // check again while locked to be certain
element = list.back();
list.pop_back();
}
mutex.unlock();
}
【问题讨论】:
-
不,你不能这样假设。您可以使用像 VC 的 concurrent_queue 这样的并发容器
-
@Fureeish 这应该是一个答案。我要补充一点,
std::list::size保证了恒定的时间复杂度,这基本上意味着需要将大小(节点数)存储在单独的变量中;我们称之为size_。std::list::empty然后可能会返回size_ == 0的内容,并且size_的并发读写会导致数据竞争,因此,UB。 -
@DanielLangr 如何测量“恒定时间”?是在单个函数调用上还是在整个程序上?
-
@curiousguy:DanielLangr 确实通过“独立于列表节点的数量”回答了您的问题,即 O(1) 的确切定义,这意味着每次调用都在不到某个恒定时间内执行,与元素数量无关。 en.wikipedia.org/wiki/Big_O_notation#Orders_of_common_functions 另一个选项(直到 C++11)将是线性 = O(n) 意思是,该大小必须计算元素(链表),这对于并发性会更糟(更明显的数据竞争比计数器上的非原子读/写)。
-
@curiousguy:以你自己的 dV 为例,时间复杂度是相同的数学 - 限制。所有这些东西要么被递归定义,要么以“存在 C 使得每个 N 的 f(N) 平均,这意味着某些输入可能需要更长的时间来处理(例如需要重新散列/重新分配),但平均而言它仍然是恒定的(假设所有可能的输入)。跨度>
标签: c++ multithreading optimization race-condition stdlist