为什么线程清理程序会抱怨此 std::ranges::views::filter 代码?
Why thread sanitizer complains about this std::ranges::views::filter code?
当 运行 此代码线程清理程序 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;}
});
}
note: 这个我知道答案,最近看一个talk才知道的,但是觉得很意外,也没有现成的问题相关,所以想分享一下。如果没有人有兴趣回答我就自己回答
[&]
在线程中-运行 lambda 应该立即发出警报。通过对线程的非const
引用来传递随机对象通常是不安全的。特别是,在标准库对象上调用非 const
成员函数不是线程安全的。
begin
不是 std::ranges::filter_view
的 const
成员函数。
constexpr iterator begin();
Expects: pred_.has_value()
.
Returns: {*this, ranges::find_if(base_, ref(*pred_))}
.
Remarks: In order to provide the amortized constant time complexity required by the range concept,
this function caches the result within the filter_view
for use on subsequent calls.
在 libstdc++ 实现中,正是这个缓存操作触发了线程清理器。
尝试使 view
常量导致编译错误。
reverse_view
也是如此。
其他范围适配器确实有 const
begin
的重载。
实际上可以通过在构建线程之前调用 view.begin()
来解决这个问题。我不知道这是否会使未定义的行为正式消失(我认为它可能应该),但它确实使消毒剂静音。
当 运行 此代码线程清理程序 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;}
});
}
note: 这个我知道答案,最近看一个talk才知道的,但是觉得很意外,也没有现成的问题相关,所以想分享一下。如果没有人有兴趣回答我就自己回答
[&]
在线程中-运行 lambda 应该立即发出警报。通过对线程的非const
引用来传递随机对象通常是不安全的。特别是,在标准库对象上调用非 const
成员函数不是线程安全的。
begin
不是 std::ranges::filter_view
的 const
成员函数。
constexpr iterator begin();
Expects:pred_.has_value()
.
Returns:{*this, ranges::find_if(base_, ref(*pred_))}
.
Remarks: In order to provide the amortized constant time complexity required by the range concept, this function caches the result within thefilter_view
for use on subsequent calls.
在 libstdc++ 实现中,正是这个缓存操作触发了线程清理器。
尝试使 view
常量导致编译错误。
reverse_view
也是如此。
其他范围适配器确实有 const
begin
的重载。
实际上可以通过在构建线程之前调用 view.begin()
来解决这个问题。我不知道这是否会使未定义的行为正式消失(我认为它可能应该),但它确实使消毒剂静音。