本文描述用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大佬
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/333694
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!