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