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 }