std::get<C++11多线程库>(03): 子线程的完整性
1 #include <QCoreApplication> 2 #include <iostream> 3 #include <thread> 4 5 /* 6 * 话题:一定要保证新线程结束之前,访问到的数据变量具有有效性。否则会产生未定义的行为和异常。 7 * 单线程代码中,对象销毁之后再去访问,也会产生未定义行为——不过,线程的生命周期增加了这个问题发生的几率。 8 * 9 * 场景1: 线程函数访问的局变量已销毁。 10 * 初始线程的函数已执行完毕,但新线程中使用了 初始函数中局部变量的指针或引用。 这种情况下, 新线程继续访问将导致未定行为。 11 * 12 * 13 * 场景2:新线程使用一个可调用对象作为线程函数,新线程结束前,可调用对象被销毁。 14 * 可调用对象会被复制一份到新线程的内存空间, 如果可调用对象中包含指针或者引用,在原可调用对象被析构后, 15 * 复制到新线程中的副本可调用对象继续使用指针或者引用,将是未定义的。 16 * 17 * 18 * 处理这种情况的常规方法: 19 * 使线程函数的功能齐全,将数据复制到线程中,而非复制到共享数据中。 20 * 如果使用一个可调用的对象作为线程函数,这个对象就会复制到线程中,而后原始对象就会立即销毁。但对于对象中包含的指针和引用还需谨慎。 21 * 使用一个能访问局部变量的函数去创建线程是一个糟糕的主意(除非十分确定线程会在函数完成前结束)。 22 * 23 * 此外,可以通过join()函数来确保线程在函数完成前结束。 24 */ 25 struct Obj{ 26 Obj(int& i):_i(i){} 27 void operator()(){ 28 for (; _i>0; --_i) //潜在访问隐患:悬空引用 29 std::cout<<"i = "<<_i<<std::endl; 30 } 31 32 private: 33 int& _i; 34 }; 35 int main(int argc, char *argv[]) 36 { 37 QCoreApplication a(argc, argv); 38 39 int x = 10000; //局变量 40 Obj obj(x); 41 std::thread t(obj); 42 //t.join(); //加入式。 等待 新线程的结束,访问变量 x 的引用 _i 不会出问题 43 t.detach(); //分离式。 不等待 新线程的结束, 访问变量 x 的引用 _i 可能会出问题 44 45 return a.exec(); 46 }