几乎每次我们处理多线程时,volatile都会被误用(至少对任何跨平台代码都是如此)。然而,我很好奇,如果使用volatile bool会给出(非确定性的)但正确的结果,如果使用在x86(-64)这样的体系结构上,其中(正确地对齐)字大小的值(正确地对齐)加载和存储的值是原子的(因此读取不能看到中间值;它总是会看到true或false)。
例如,考虑如下多线程线性搜索:
#include <algorithm>
#include <future>
#include <thread>
#include <vector>
namespace detail
{
template <typename Iterator>
std::vector<std::pair<Iterator, Iterator>> split_range(
Iterator begin, Iterator end, unsigned nranges, std::random_access_iterator_tag
)
{
std::vector<std::pair<Iterator, Iterator>> split(nranges);
auto diff = end - begin;
for(auto i = 0U; i < nranges - 1; ++i) {
split[i] = std::make_pair(begin, begin + (diff / nranges));
begin += (diff / nranges);
}
split[nranges - 1] = std::make_pair(begin, end);
return split;
}
template <typename Iterator>
Iterator async_find_impl(
std::pair<Iterator, Iterator> range,
typename std::iterator_traits<Iterator>::value_type value,
volatile bool* finished
)
{
auto begin = range.first;
auto end = range.second;
while(!(*finished) && (begin != end)) {
if(*begin == value) {
*finished = true;
return begin;
}
++begin;
}
return end;
}
}
template <typename Iterator>
std::vector<std::pair<Iterator, Iterator>> split_range(
Iterator begin, Iterator end, unsigned ranges
)
{
return detail::split_range(
begin, end, ranges,
typename std::iterator_traits<Iterator>::iterator_category()
);
}
template <typename Iterator>
Iterator async_find(
Iterator begin, Iterator end,
typename std::iterator_traits<Iterator>::value_type value
)
{
volatile bool found = false;
const static auto default_launch = 4U;
unsigned to_launch = std::thread::hardware_concurrency();
if(to_launch == 0) { to_launch = default_launch; }
std::vector<std::future<Iterator>> futures;
auto ranges = split_range(begin, end, to_launch);
for(auto&& range : ranges) {
auto func = [&]() { return detail::async_find_impl(range, value, &found); };
futures.emplace_back(std::async(func));
}
std::vector<Iterator> results;
for(auto&& future : futures) {
results.emplace_back(future.get());
}
for(auto i = 0U; i < results.size(); ++i) {
if(results[i] != ranges[i].second) {
return results[i];
}
}
return end;
}显然,这样做的正确的跨平台方式是使用std::atomic<bool>。但是,考虑到上面列出的假设,这是否是“正确的”(在这里,更正是返回指向给定值的任何Iterator;如果有多种可能性,本质上是不确定的返回哪一个)?
发布于 2016-02-03 14:44:20
C和C++中的Atomics解决了的三个问题:撕裂、重新排序和可见性。当线程开关发生在需要多个总线周期的读或写中间(例如,在32位处理器上运行时写入64位值),而新运行的线程分别写入或读取该值时,则会发生撕裂。重新排序可以由编译器和由处理器完成。出现可见性问题是因为每个处理器都有自己的本地数据缓存;来自一个处理器的写入在另一个处理器中不可见,直到新值被写入主内存,然后读入另一个处理器的本地缓存。
bool值可能存储在足够小的内存块中,不会被撕开,尽管这不是必需的。volatile告诉编译器不要重新排序读和写(也不要删除它们)。这两种方法都不能说明可见性。因此,使用volatile bool 可能会为您提供所需的结果,尽管无法保证何时可以看到更新的值。但何必费心呢?而不是手工滚动你自己的一半,只使用atomic<bool>。它解决了所有三个问题,并将编写来利用底层硬件,也许是以您没有想到的方式编写的。
https://stackoverflow.com/questions/35169096
复制相似问题