首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >具有强内存模型的平台上易失性和多线程处理

具有强内存模型的平台上易失性和多线程处理
EN

Stack Overflow用户
提问于 2016-02-03 05:22:53
回答 1查看 100关注 0票数 0

几乎每次我们处理多线程时,volatile都会被误用(至少对任何跨平台代码都是如此)。然而,我很好奇,如果使用volatile bool会给出(非确定性的)但正确的结果,如果使用在x86(-64)这样的体系结构上,其中(正确地对齐)字大小的值(正确地对齐)加载和存储的值是原子的(因此读取不能看到中间值;它总是会看到truefalse)。

例如,考虑如下多线程线性搜索:

代码语言:javascript
复制
#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;如果有多种可能性,本质上是不确定的返回哪一个)?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-02-03 14:44:20

C和C++中的Atomics解决了的三个问题:撕裂、重新排序和可见性。当线程开关发生在需要多个总线周期的读或写中间(例如,在32位处理器上运行时写入64位值),而新运行的线程分别写入或读取该值时,则会发生撕裂。重新排序可以由编译器由处理器完成。出现可见性问题是因为每个处理器都有自己的本地数据缓存;来自一个处理器的写入在另一个处理器中不可见,直到新值被写入主内存,然后读入另一个处理器的本地缓存。

bool值可能存储在足够小的内存块中,不会被撕开,尽管这不是必需的。volatile告诉编译器不要重新排序读和写(也不要删除它们)。这两种方法都不能说明可见性。因此,使用volatile bool 可能会为您提供所需的结果,尽管无法保证何时可以看到更新的值。但何必费心呢?而不是手工滚动你自己的一半,只使用atomic<bool>。它解决了所有三个问题,并将编写来利用底层硬件,也许是以您没有想到的方式编写的。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35169096

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档