1 #ifndef USE_CODE_PROTECTED_DATA_H
2 #define USE_CODE_PROTECTED_DATA_H
3
4 /*
5 * 话题1:使用互斥量保护共享数据
6 *
7 * 接下来学习第二个小话题:用代码来保护共享数据
8 *
9 * 从第一个小话题可以看到,std::mutex即便可以让线程同步的访问共享数据,设计不好时,同样会出现数据得不到保护的情况。
10 * 检查指针或引用很容易,只要没有成员函数通过返回值或者输出参数的形式,向其调用者返回指向受保护数据的指针或引用,数据就是安全的。
11 *
12 * 如果你还想深究,就没这么简单了。
13 * 确保成员函数不会传出指针或引用的同时,检查成员函数是否通过指针或引用的方式来调用也是很重要的。
14 * 函数可能会把共享数据的指针或者引用,存放在没有互斥量保护的区域内,这样就很危险。
15 * 更危险的是:将保护数据作为一个运行时参数,如同下面清单中所示那样。
16 */
17
18 /*
19 * 先来看一下,上面描述的共享数据失去保护的情况。
20 */
21
22 #include <string>
23 #include <mutex>
24 class data{
25 std::string s;
26 public:
27 data(std::string s){
28 this->s = s;
29 }
30
31 void do_data_modify(std::string s){
32 this->s = s;
33 }
34 };
35
36 class data_Wrapper{
37 data m_data;
38 std::mutex m_mutex;
39
40 public:
41 data_Wrapper(data d):m_data(d){}
42 template<typename FuncPtr>
43 void process_data(FuncPtr func){
44 std::lock_guard<std::mutex> lk(m_mutex);
45 func(&m_data);
46 }
47 };
48
49 /*
50 * 可以看出啊, 虽然 std::mutex 可以保护共享数据,并能保证多线程可以同步的安全的访问共享数据,
51 * 但是,完全可能发生,由于代码设计的不合理, 把本该受到保护的共享数据,以指针或引用的形式交给了不受保护的区域。
52 * 在不受保护的区域内对共享数据进行修改,这种情况在多线程环境中终将是会发生车祸的。
53 *
54 * 虽然这是在使用互斥量保护共享数据时常犯的错误,但绝不仅仅是一个潜在的陷阱而已。
55 * 下一节中,你将会看到,即便是使用了互斥量对数据进行了保护,条件竞争依旧可能存在。
56 *
57 * 如何解决呢?
58 */
59
60
61 /*
62 * 第二个小话题:用代码来保护共享数据, 使用 std::mutex 也会出现共享数据失去保护的情况。
63 */
64
65 #include <QCoreApplication>
66 #include "Use_code_protected_data.h"
67 data * non_mutex_protected_data = nullptr;
68 void malicious_func(data *ptrData){
69 non_mutex_protected_data = ptrData;
70 }
71
72 int main(int argc, char *argv[])
73 {
74 QCoreApplication a(argc, argv);
75
76 data d("healthy data");
77 data_Wrapper dataWrapper(d);
78 dataWrapper.process_data(malicious_func);
79 non_mutex_protected_data->do_data_modify("magic data"); //共享数据在毫无保护的情况下发生了修改。
80
81 return a.exec();
82 }
83
84
85 class Use_code_protected_data
86 {
87 public:
88 Use_code_protected_data();
89 };
90
91 #endif // USE_CODE_PROTECTED_DATA_H