Effective Modern C++: 多线程与资源互锁 [二]

本文描述用std::atomic实现线程资源互锁

std::atomic用于实现程序里的原子操作,有关原子操作在《C++ Concurrency in Action》中的介绍:

原子操作是一类不可分割的操作,当这样操作在任意线程中进行一半的时候,你是不能查看的;它的状态要不就是完成,要不就是未完成。如果从对象中读取一个值的操作是原子的, 并且对对象的所有修改也都是原子的话,那么加载操作要不就会检索对象初始化的值,要不就将值存在某一次修改中。

另一方面,非原子操作可能会被视为由一个线程完成一半的操作。如果这种是一个存储操作,那么其他线程看到的,可能既不是存储前的值,也可能不是已存储的值。如果非原子操作是一个加载操作,那么它可能会去检索对象的部分成员,或是在另一个线程修改了对象的 值后,对对象进行检索;所以,检索出来的值可能既不是第一个值,也不是第二个值,可能是某种两者结合的值。这就是一个简单的条件竞争,但是这种级别的竞争会构成数据竞争,且会伴有有未定义行为。

 

1. 用std::atomic_flag实现

  std::atomic_flag是最简单的标准原子类型,必须用ATOMIC_FLAG_INIT初始化,支持两种原子操作:

  • test_and_set, 如果atomic_flag对象被设置,则返回true; 如果atomic_flag对象未被设置,则设置之,返回false
  • clear. 清楚atomic_flag对象

  std::atomic_flag可用于多线程之间的同步操作,类似于linux中的信号量。使用atomic_flag可实现mutex.

 1 class Point{
 2 private:
 3     double x;
 4     double y;
 5     mutable std::atomic_flag lock = ATOMIC_FLAG_INIT;
 6 public:
 7     Point(double _x, double _y){
 8         x = _x;
 9         y = _y;
10     }
11     void calculateAndPrintDistance() const{
12         while(lock.test_and_set());
13         double result = pow((x*x + y*y), 0.5);
14         std::cout << result << std::endl;
15         lock.clear();
16     }
17     void setCoordinate(double value){
18         x = value;
19         y = value;
20     }
21 };
22 
23 int main(){
24 
25     const int loop = 100;
26     Point p(0, 0);
27     std::thread threads[loop];
28     for(int i=0; i<loop; i++){
29         p.setCoordinate(i);
30         threads[i] = std::thread(&Point::calculateAndPrintDistance, std::ref(p));
31     }
32     for(int i=0; i<loop; i++){
33         threads[i].join();
34     }
35     return 0;
36 }

2.用std::atomic<T*> 对对象进行封装,std::atomic封装的对象不会进行竞争-冒险。

atomic对象具有两个方法,store和load,分别用于存取数据。

 1 class Point{
 2 private:
 3     double x;
 4     double y;
 5     mutable std::atomic<double> distance;
 6 public:
 7     Point(double _x, double _y){
 8         x = _x;
 9         y = _y;
10     }
11     void calculateDistance() {
12         distance.store(pow((x*x + y*y), 0.5));
13     }
14 
15     void PrintDistance() const{
16         std::cout << distance.load() << std::endl;
17     }
18     void setCoordinate(double value){
19         x = value;
20         y = value;
21     }
22 };
23 
24 int main(){
25 
26     const int loop = 100;
27     Point p(0, 0);
28     std::thread threadsCalc[loop];
29     std::thread threadsPrint[loop];
30     for(int i=0; i<loop; i++){
31         p.setCoordinate(i);
32         threadsCalc[i] = std::thread(&Point::calculateDistance, std::ref(p));
33         threadsPrint[i] = std::thread(&Point::PrintDistance, std::ref(p));
34     }
35     for(int i=0; i<loop; i++){
36         threadsCalc[i].join();
37         threadsPrint[i].join();
38     }
39     return 0;
40 }

(上面这段代码作为示例来表述这一特征不太合适,暂时想不到什么usecase来证实其功能 )

原文链接: https://www.cnblogs.com/Asp1rant/p/12430751.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    Effective Modern C++: 多线程与资源互锁 [二]

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/333694

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年3月1日 下午9:18
下一篇 2023年3月1日 下午9:18

相关推荐