【问题标题】:Why thread sanitizer complains about this std::ranges::views::filter code?为什么线程清理程序抱怨这个 std::ranges::views::filter 代码?
【发布时间】:2020-11-15 23:10:49
【问题描述】:

在运行此代码时,线程清理程序complains 关于数据竞争。 为什么?

#include <iostream>
#include <ranges>
#include <thread>
#include <vector>

int main(){
    std::vector v{11,22,33,44,55,66};
    auto view = v | std::ranges::views::filter([](const auto x){
        return x>47;
    });
    std::jthread jt1([&]{
    int sum =0;
    for (const auto& val: view) {sum+=val;}
    });    
    std::jthread jt2([&]{
    int sum =0;
    for (const auto& val: view) {sum+=val;}
    });
}

注意:我知道这个问题的答案,我最近在看一个演讲时了解到它,但我发现它非常令人惊讶,并且没有与此相关的问题,所以我想分享它。如果没有人有兴趣回答,我会自己回答。

【问题讨论】:

    标签: c++ race-condition c++20 range-v3


    【解决方案1】:

    [&amp;] 在线程运行的 lambda 中应该立即响起警报。通过非const 对线程的引用传递随机对象通常是不安全的。特别是,在标准库对象上调用非const 成员函数不是线程安全的。

    begin 不是std::ranges::filter_view 的const 成员函数。

    constexpr iterator begin();
    期望:pred_.has_value().
    返回:{*this, ranges::find_if(base_, ref(*pred_))}.
    备注:为了提供范围概念所需的摊销常数时间复杂度, 此函数将结果缓存在filter_view 中,以供后续调用使用。

    在 libstdc++ 实现中,正是这种精确的缓存操作触发了线程清理程序。

    尝试将view 设为常量会导致编译错误。

    reverse_view 也是如此。

    其他范围适配器确实对begin 有const 重载。

    实际上可以通过在构造线程之前调用view.begin() 来解决这个问题。我不知道这是否会使未定义的行为正式消失(我认为它可能应该),但它确实使消毒剂静音。

    【讨论】:

      猜你喜欢
      • 2022-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-16
      • 1970-01-01
      • 1970-01-01
      • 2021-07-05
      相关资源
      最近更新 更多