是否需要同步关系来避免函数的重复调用?

问题描述 投票:0回答:1

考虑这个例子:

#include <iostream>
#include <atomic>
#include <random>
#include <thread>
int need_close(){
   random_device rd;
   std::mt19937 gen(rd());
   uniform_int_distribution<int> distribute(0, 2);
   return distribute(gen);
}
void invoke(std::atomic<bool>& is_close){
    if(is_close.load(std::memory_order::relaxed)){  // #1
        return;
    }
    auto r = need_close();
    if(r==0){
        is_close.store(true,std::memory_order::relaxed);
    }
}
int main(){
    std::atomic<bool> is_close{false};
    std::thread t1([&](){
        for(auto i = 0; i<100000;i++)
        invoke(is_close);
    });
    std::thread t2([&](){
        for(auto i = 0; i<100000;i++)
        invoke(is_close);
    });
    t1.join();
    t2.join();
}

在此示例中,一旦线程看到

relaxed
不是立即,而是在某个时间线程可以读取 
need_close
),is_close == true
 的顺序是否足以避免调用 
is_close==true
?在此示例中,似乎我不需要同步来避免数据争用,因为此示例中没有冲突操作。但是,从编译器实现来看,编译器可能会将
#1
重新排序到它后面的任何位置,因为这里使用了
relaxed
,例如,如果
#1
移动到
need_close
的调用点之后的某个位置,则
即使 
need_close
 设置为 
is_close
true
仍将始终被再次调用,这是不期望的。所以,我想知道是否需要
Acquire/Release
排序以避免编译器重新排序代码以使逻辑符合预期?

c++ language-lawyer atomic
1个回答
0
投票

您如何知道您是否可以前往拨打

need_close()
的分行?通过读取
is_close
中的值,在不知道该值的情况下不可能这样做,因此即使在宽松的排序约束下,编译器也无法在调用
need_close()
后对 #1 进行重新排序,但这并不能解决您的问题。

排序不会阻止函数在读取和

need_close()
之间被不同线程调用两次,另一个线程可以执行这两个操作,因此
need_close()
将被调用两次。

您根本无法阻止

need_close()
被呼叫两次。通过使其逻辑并发友好或使用互斥体,它必须对并发调用是安全的。

© www.soinside.com 2019 - 2024. All rights reserved.