【C++多线程】共享数据保护

  保护共享数据的最基本的方式,是使用C++标准库提供的互斥量(头文件<mutex>)。当访问共享数据前,使用互斥量将相关数据锁住,再当访问结束后,再将数据解锁。线程库需要保证,当一个线程使用特定互斥量锁住共享数据时,其他的线程想要访问锁住的数据,都必须等到之前那个线程对数据进行解锁后,才能进行访问。这就保证了所有线程能看到共享数据,而不破坏不变量。

  C++中通过实例化 std::mutex 创建互斥量,通过调用成员函数lock()进行上锁,unlock()进行解锁。

 1 #include <iostream>
 2 #include <thread>
 3 #include <mutex>
 4 #include <list>
 5 
 6 using namespace std;
 7 
 8 class A {
 9 public:
10     void input()
11     {
12         for (int i = 0; i < 1000; i++)
13         {
14             my_mutex.lock();
15             cout << "加入数据:" << i << endl;
16             ilst.push_back(i);
17             my_mutex.unlock();
18         }
19 
20     }
21 
22     void output()
23     {
24         for (int i = 0; i < 1000; i++)
25         {
26             my_mutex.lock();
27             if (!ilst.empty())
28             {
29                 cout << "读读读读出数据:" << ilst.front() << endl;
30                 ilst.pop_front();
31                 my_mutex.unlock();
32             }
33             else
34                 my_mutex.unlock();
35         }
36     }
37 
38 private:
39     list<int> ilst;
40     mutex my_mutex;
41 };
42 
43 int  main()
44 {
45     A a;
46     thread t1(&A::input, &a); //注意此处需要传入的是对象地址,否则数据没办法共享
47     thread t2(&A::output, &a);
48     t1.join();
49     t2.join();
50     return 0;
51 }

  C++标准库还为互斥量提供了一个RAII语法的模板类 std::lack_guard<T> ,其会在构造的时候提供已锁的互斥量,并在析构的时候进行解锁,从而保证了一个已锁的互斥量总是会被正确的解锁。

 1 #include <iostream>
 2 #include <thread>
 3 #include <mutex>
 4 #include <list>
 5 
 6 using namespace std;
 7 
 8 class A {
 9 public:
10     void input()
11     {
12         for (int i = 0; i < 1000; i++)
13         {
14             lock_guard<mutex> guard(my_mutex);
15             /*my_mutex.lock();*/
16             cout << "加入数据:" << i << endl;
17             ilst.push_back(i);
18            /* my_mutex.unlock();*/
19         }
20 
21     }
22 
23     void output()
24     {
25         for (int i = 0; i < 1000; i++)
26         {
27             lock_guard<mutex> guard(my_mutex);
28             /*my_mutex.lock();*/
29             if (!ilst.empty())
30             {
31                 cout << "读读读读出数据:" << ilst.front() << endl;
32                 ilst.pop_front();
33                 /*my_mutex.unlock();*/
34             }
35             /*else
36                 my_mutex.unlock();*/
37         }
38     }
39 
40 private:
41     list<int> ilst;
42     mutex my_mutex;
43 };
44 
45 int  main()
46 {
47     A a;
48     thread t1(&A::input, &a); //注意此处需要传入的是对象地址,否则数据没办法共享
49     thread t2(&A::output, &a);
50     t1.join();
51     t2.join();
52     return 0;
53 }

  但互斥量自身也有问题。

  当其中一个成员函数返回的是保护数据的指针或引用时,会破坏对数据的保护。具有访问能力的指针或引用可以访问(并可能修改)被保护的数据,而不会被互斥锁限制。所以切勿将受保护数据的指针或引用传递到互斥锁作用域之外,无论是函数返回值,还是存储在外部可见内存,亦或是以参数的形式传递到用户提供的函数中去。

  另外还会造成死锁,或是对数据保护的太多(或太少)的问题

原文地址:https://www.cnblogs.com/chen-cs/p/13060353.html