文章大图来源: pixiv_id=82332929
1 主线程与子线程
在多线程程序中,通常情况下有一个主线程以及若干个子线程。主线程就是我们执行主函数 main()
的线程,子线程是我们在主线程的函数 main()
中(或者可以在其他现有线程的函数中中)创建的线程。
主线程和子线程在同一时间段内同时进行,是并发(并行)运行的。在这个过程中,会有以下几种情况:
-
主线程先运行结束
-
子线程先运行结束
-
主线程与子线程同时结束
有时候我们需要让子线程运行结束,主线程才能结束;有时我们可以选择不等待子线程运行结束,主线程运行完后整个程序就可以结束了。
需要注意的是,在不等待子线程运行结束的情况下,主线程运行结束后子线程并不会立即停止,而是 会进入后台运行。如果子线程所需资源(如内存)仍然有效,并且没有被外部因素(如其他线程发送终止信号等)强制终止,子线程还是会继续运行。只不过是在后台运行。
2 std::thread::join
join()
是 std::thread 类的一个成员函数,作用是等待线程完成。我们在主线程中创建一个子线程 t
,并在这个线程对象上调用 join()
函数,此时主线程会被 阻塞,直到调用 join()
函数的子线程 t
执行完。
我们可以写一个简单的线程接口函数 foo()
,并在其中模拟做了一些事情。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <iostream> #include <thread>
void foo(){ std::cout << "foo(): thread starts." << "\n"; std::cout << "foo(): MarisaMagic is doing sth." << "\n"; std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "foo(): thread ends." << "\n"; }
int main(){ std::cout << "main(): thread starts." << "\n";
std::thread t(foo); t.join();
std::cout << "main(): thread ends." << "\n";
return 0; }
|
运行结果如下。可以看出,foo()
子线程运行结束后,main()
主线程才结束。重复多次运行后结果依旧如此。

在上面的程序中,为了让子线程运行时间长一些,方便之后更好演示,子线程接口函数中使用了 std::this_thread::sleep_for()
函数,作用是让当前线程休息一段时间。std::this_thread::sleep_for()
接收的是一个 std::chrono::duration 类型的参数(通常为秒(std::chrono::seconds)、毫秒(std::chrono::milliseconds)等)。此处,我让子线程休息了 1 秒钟。
3 std::thread::detach
detach()
也是 std::thread 类的一个成员函数,称为 分离线程函数。我们在主线程中创建一个子线程 t
,并在这个线程对象上调用 detach()
函数后,main()
主线程不会等待子线程 t
运行结束。
主线程执行可能是比较快的,会先于子线程结束,而分离的子线程 t
可以在后台独立运行,和主线程相互独立。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <iostream> #include <thread>
void foo(){ std::cout << "foo(): thread starts." << "\n"; std::cout << "foo(): MarisaMagic is doing sth." << "\n"; std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "foo(): thread ends." << "\n"; }
int main(){ std::cout << "main(): thread starts." << "\n";
std::thread t(foo); t.detach();
std::cout << "main(): thread ends." << "\n";
return 0; }
|
运行结果如下。可以看出,主线程运行得比较快,而子线程需要 1 秒多的运行时间,主线程并没有等待子线程运行结束就已经先结束了。

4 join() 和 detach() 不可多次调用
当一个子线程调用了一次 join()
后,就不能再次调用 join()
了。join()
是等待线程执行完成,执行完之后,子线程也自然结束,再次调用 join()
是不可行的,当然调用 detach()
也没有意义,会导致未定义行为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <iostream> #include <thread>
void foo(){ std::cout << "foo(): thread starts." << "\n"; std::cout << "foo(): MarisaMagic is doing sth." << "\n"; std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "foo(): thread ends." << "\n"; }
int main(){ std::cout << "main(): thread starts." << "\n";
std::thread t(foo); t.join();
try{ t.join(); }catch(...){ std::cout << "ERROR: multiple join." << "\n"; }
std::cout << "main(): thread ends." << "\n";
return 0; }
|

同理,一个子线程调用了一次 detach()
被分离后,也不能再通过 std::thread 对象来对该线程进行控制(不能再次调用 join()
或 detach()
)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <iostream> #include <thread>
void foo(){ std::cout << "foo(): thread starts." << "\n"; std::cout << "foo(): MarisaMagic is doing sth." << "\n"; std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "foo(): thread ends." << "\n"; }
int main(){ std::cout << "main(): thread starts." << "\n";
std::thread t(foo); t.detach();
try{ t.detach(); }catch(...){ std::cout << "ERROR: multiple detach." << "\n"; }
std::cout << "main(): thread ends." << "\n";
return 0; }
|

5 std::thread::joinable
joinable()
是 std::thread 类的一个成员函数,用于检查线程是否可以被 join
或者 detach
。如果线程是可以调用 join
或者 detach
的,那么 joinable()
函数返回值为 true
;否则为 false
。
一个线程是不可 join
也不可 detach
的(joinable()
函数返回值为 false
,状态为 nonjoinable
),大致分为三种情况:
我们可以用如下程序进行测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <iostream> #include <thread>
void foo(){ return; }
int main(){ std::thread t1; std::cout << t1.joinable() << "\n";
std::thread t2(foo); t2.join(); std::cout << t2.joinable() << "\n";
std::thread t3(foo); t3.detach(); std::cout << t3.joinable() << "\n";
return 0; }
|
输出三个 0
,返回值均为 false
,表示三种情况下线程都是不可 join
也不可 detach
的。

在实际运用过程中,为了保险起见,我们有时候可以在对线程进行 join
或者detach
操作之前先检查是否 joinable
,避免出现未定义行为。
1 2 3 4
| std::thread t(foo); if(t.joinable()){ t.join(); }
|
参考
-
cplusplus reference std::thread
-
C++11多线程join()和detach()的理解
-
C++11 线程对象创建后既不join()也不detach()的后果