list::empty() 多线程行为?
list::empty() multi-threaded behavior?
我有一个列表,我希望不同的线程从中获取元素。为了避免在列表为空时锁定保护列表的互斥锁,我在锁定之前检查 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();
}
有一个读取和写入(很可能是 std::list
的 size
成员,如果我们假设它是这样命名的话)在 方面不同步彼此。想象一下,一个线程调用 empty()
(在你的外部 if()
中),而另一个线程进入内部 if()
并执行 pop_back()
。然后您正在读取一个可能正在修改的变量。这是未定义的行为。
It's okay if the call to list::empty()
isn't right 100% of the time.
不,这样不行。如果您在某种同步机制(锁定互斥锁)之外检查列表是否为空,那么您就会发生数据竞争。发生数据竞争意味着您有未定义的行为。具有未定义的行为意味着我们无法再对程序进行推理,并且您获得的任何输出都是 "correct".
如果您重视自己的理智,您将承受性能损失并在检查之前锁定互斥体。也就是说,该列表甚至可能不是适合您的容器。如果您可以让我们确切知道您用它做什么,我们也许可以建议一个更好的容器。
举个可能出错的例子:
足够聪明的编译器可以看到 mutex.lock()
不可能更改 list.empty()
return 值,因此完全跳过内部 if
检查,最终导致 pop_back
在列表中,在第一个 if
.
之后删除了最后一个元素
为什么它能做到? list.empty()
中没有同步,因此如果它被同时更改,将构成数据竞争。该标准规定程序不应存在数据竞争,因此编译器将认为这是理所当然的(否则它几乎不会执行任何优化)。因此,它可以假设一个单线程的角度来看非同步 list.empty()
并得出结论,它必须保持不变。
这只是可能破坏您的代码的几种优化(或硬件行为)之一。
我有一个列表,我希望不同的线程从中获取元素。为了避免在列表为空时锁定保护列表的互斥锁,我在锁定之前检查 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();
}
有一个读取和写入(很可能是 std::list
的 size
成员,如果我们假设它是这样命名的话)在 方面不同步彼此。想象一下,一个线程调用 empty()
(在你的外部 if()
中),而另一个线程进入内部 if()
并执行 pop_back()
。然后您正在读取一个可能正在修改的变量。这是未定义的行为。
It's okay if the call to
list::empty()
isn't right 100% of the time.
不,这样不行。如果您在某种同步机制(锁定互斥锁)之外检查列表是否为空,那么您就会发生数据竞争。发生数据竞争意味着您有未定义的行为。具有未定义的行为意味着我们无法再对程序进行推理,并且您获得的任何输出都是 "correct".
如果您重视自己的理智,您将承受性能损失并在检查之前锁定互斥体。也就是说,该列表甚至可能不是适合您的容器。如果您可以让我们确切知道您用它做什么,我们也许可以建议一个更好的容器。
举个可能出错的例子:
足够聪明的编译器可以看到 mutex.lock()
不可能更改 list.empty()
return 值,因此完全跳过内部 if
检查,最终导致 pop_back
在列表中,在第一个 if
.
为什么它能做到? list.empty()
中没有同步,因此如果它被同时更改,将构成数据竞争。该标准规定程序不应存在数据竞争,因此编译器将认为这是理所当然的(否则它几乎不会执行任何优化)。因此,它可以假设一个单线程的角度来看非同步 list.empty()
并得出结论,它必须保持不变。
这只是可能破坏您的代码的几种优化(或硬件行为)之一。