一:概述
项目中经常用遇到多线程操作共享数据问题,常用的处理方式是对共享数据进行加锁,如果多线程操作共享变量也同样采用这种方式。
为什么要对共享变量加锁或使用原子操作?如两个线程操作同一变量过程中,一个线程执行过程中可能被内核临时挂起,这就是线程切换,当内核再次切换到该线程时,之前的数据可能已被修改,不能保证原子操作。
C++11提供了个原子的类和方法atomic,保证了多线程对变量原子性操作,相比加锁机制mutex.locak(),mutex.unlocak(),性能有几倍的提升。
所需头文件<atomic>
二:错误代码
1 //全局变量 2 int g_num = 0; 3 4 void fun() 5 { 6 for (int i = 0; i < 10000000; i++) 7 { 8 g_num++; 9 } 10 return ; 11 } 12 13 int main() 14 { 15 //创建线程1 16 thread t1(fun); 17 //创建线程2 18 thread t2(fun); 19 t1.join(); 20 t2.join(); 21 22 cout << g_num << endl; 23 getchar(); 24 return 1; 25 }
应该输出结果20000000,实际每次结果都不一样,总是小于该值,正是由于多线程操作同一变量而没有保证原子性导致的。
三:加锁代码
1 //全局变量 2 int g_num = 0; 3 mutex m_mutex; 4 5 void fun() 6 { 7 for (int i = 0; i < 10000000; i++) 8 { 9 m_mutex.lock(); 10 g_num++; 11 m_mutex.unlock(); 12 } 13 return ; 14 } 15 16 int main() 17 { 18 //获取当前毫秒时间戳 19 typedef chrono::time_point<chrono::system_clock, chrono::milliseconds> microClock_type; 20 microClock_type tp1 = chrono::time_point_cast<chrono::milliseconds>(chrono::system_clock::now()); 21 long long time1 = tp1.time_since_epoch().count(); 22 23 //创建线程 24 thread t1(fun); 25 thread t2(fun); 26 t1.join(); 27 t2.join(); 28 29 cout << "总数:" << g_num << endl; 30 31 //获取当前毫秒时间戳 32 microClock_type tp2 = chrono::time_point_cast<chrono::milliseconds>(chrono::system_clock::now()); 33 long long time2 = tp2.time_since_epoch().count(); 34 cout << "耗时:" << time2 - time1 << "ms" << endl; 35 36 getchar(); 37 return 1; 38 }
执行结果:多次测试输出均为20000000,耗时在3.8s左右
四:atomic原子操作代码
1 //全局变量 2 atomic<int> g_num = 0; 3 4 void fun() 5 { 6 for (int i = 0; i < 10000000; i++) 7 { 8 g_num++; 9 } 10 return ; 11 } 12 13 int main() 14 { 15 //获取当前毫秒时间戳 16 typedef chrono::time_point<chrono::system_clock, chrono::milliseconds> microClock_type; 17 microClock_type tp1 = chrono::time_point_cast<chrono::milliseconds>(chrono::system_clock::now()); 18 long long time1 = tp1.time_since_epoch().count(); 19 20 //创建线程 21 thread t1(fun); 22 thread t2(fun); 23 t1.join(); 24 t2.join(); 25 26 cout << "总数:" << g_num << endl; 27 28 //获取当前毫秒时间戳 29 microClock_type tp2 = chrono::time_point_cast<chrono::milliseconds>(chrono::system_clock::now()); 30 long long time2 = tp2.time_since_epoch().count(); 31 cout << "耗时:" << time2 - time1 << "ms" << endl; 32 33 getchar(); 34 return 1; 35 }
执行结果:多次测试输出均为20000000,耗时在1.3s左右
五:总结
c++11的原子类atomic相比使用加锁机制性能有2~3倍提升,对于共享变量能用原子类型的就不要再用加锁机制了。
扫码关注公众号
专注分享Java,C/C++,STL,Spring框架,mybatis框架,mysql,redis,分布式,高并发,设计模式,爬虫,docker,shell编程等相关技术,在这里一起探讨,一起学习,一起进步,不定期分享视频书籍资源,充分利用碎片化时间,让我们的技术之路更加有乐趣。