线程同步是指同一进程中的多个线程互相协调工作从而达到一致性。之所以需要线程同步,是因为多个线程同时对一个数据对象进行修改操作时,可能会对数据造成破坏,下面是多个线程同时修改同一数据造成破坏的例子:
1 #include <thread>
2 #include <iostream>
3
4 void Fun_1(unsigned int &counter);
5 void Fun_2(unsigned int &counter);
6
7 int main()
8 {
9 unsigned int counter = 0;
10 std::thread thrd_1(Fun_1, counter);
11 std::thread thrd_2(Fun_2, counter);
12 thrd_1.join();
13 thrd_2.join();
14 system("pause");
15 return 0;
16 }
17
18 void Fun_1(unsigned int &counter)
19 {
20 while (true)
21 {
22 ++counter;
23 if (counter < 1000)
24 {
25 std::cout << "Function 1 counting " << counter << "...\n";
26 }
27 else
28 {
29 break;
30 }
31 }
32 }
33
34 void Fun_2(unsigned int &counter)
35 {
36 while (true)
37 {
38 ++counter;
39 if (counter < 1000)
40 {
41 std::cout << "Function 2 counting " << counter << "...\n";
42 }
43 else
44 {
45 break;
46 }
47 }
48 }
运行结果如图所示:
显然输出的结果存在问题,变量并没有按顺序递增,所以线程同步是很重要的。在这里记录三种线程同步的方式:
①使用C++标准库的thread、mutex头文件:
1 #include <thread>
2 #include <mutex>
3 #include <iostream>
4
5 void Fun_1();
6 void Fun_2();
7
8 unsigned int counter = 0;
9 std::mutex mtx;
10
11 int main()
12 {
13 std::thread thrd_1(Fun_1);
14 std::thread thrd_2(Fun_2);
15 thrd_1.join();
16 thrd_2.join();
17 system("pause");
18 return 0;
19 }
20
21 void Fun_1()
22 {
23 while (true)
24 {
25 std::lock_guard<std::mutex> mtx_locker(mtx);
26 ++counter;
27 if (counter < 1000)
28 {
29 std::cout << "Function 1 counting " << counter << "...\n";
30 }
31 else
32 {
33 break;
34 }
35 }
36 }
37
38 void Fun_2()
39 {
40 while (true)
41 {
42 std::lock_guard<std::mutex> mtx_locker(mtx);
43 ++counter;
44 if (counter < 1000)
45 {
46 std::cout << "Function 2 counting " << counter << "...\n";
47 }
48 else
49 {
50 break;
51 }
52 }
53 }
std::lock_guard<std::mutex> mtx_locker(mtx); 在C++中,通过构造std::mutex的实例来创建互斥元,可通过调用其成员函数lock()和unlock()来实现加锁和解锁,然后这是不推荐的做法,因为这要求程序员在离开函数的每条代码路径上都调用unlock(),包括由于异常所导致的在内。作为替代,标准库提供了std::lock_guard类模板,实现了互斥元的RAII惯用语法(资源获取即初始化)。该对象在构造时锁定所给的互斥元,析构时解锁该互斥元,从而保证被锁定的互斥元始终被正确解锁。代码运行结果如下图所示,可见得到了正确的结果。