C++ 11 多线程 (一)
- thread 类
- 方法列表
函数名 | 形式 | 备注 |
---|---|---|
构造函数 | -默认构造函数thread() -初始化构造函数thread(Fn&& fn, Args&&… args); -移动构造函数thread (thread&& x) noexcept; | 无法使用拷贝构造函数 |
析构器 | ~thread() | |
= | -move: thread& operator= (thread&& rhs) noexcept; | -If the object is currently not joinable, it acquires the thread of execution represented by rhs (if any). -If it is joinable, terminate() is called. -After the call, rhs no longer represents any thread of execution (as if default-constructed). -无法使用移动赋值 |
获取id | get_id() | 若object目前是joinable,返回unique id -若object目前不是joinable,返回thread::id的默认构造对象 |
查看join状态 | joinable() | false reason: -thread是默认构造,例如thread t; -使用过移动构造函数或移动赋值 -使用了join() 或 detach() |
join() | -thread t(func);t.join(); -使用函数后,线程t变为non-joinable,t可以被安全的destroy -调用t的线程阻塞,直到t返回 | |
detach() | 与join()区别:调用t的线程执行与t的执行互相独立 | |
其他 | -swap(thread& x) -native_handle() -static unsigned hardware_concurrency() noexcept; |
- 代码实践
3.1. join()方法
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <future>
#include <iostream>
#include <Windows.h>
using namespace std;
int Num = 100;
void thread_task1() {
for (int i = 0; i < 5; ++i) {
cout << "Thread_Task1: " << i << " Num is" << Num-- << endl;
Sleep(100);
}
}
void thread_task2() {
for (int i = 0; i < 5; ++i) {
cout << "Thread_Task2: " << i << " Num is" << Num-- << endl;
Sleep(100);
}
}
void main() {
thread t1 = thread(thread_task1);
cout << "t1 thread id is " << t1.get_id() << endl;
thread t2 = thread(thread_task2);
cout << "t2 thread id is " << t2.get_id() << endl;
t1.join();
t2.join();
for (int i = 0; i < 5; ++i) {
cout << "Main is working" << endl;
}
Sleep(10000);
system("pause");
}
运行结果
从图中可以看到
- 创建thread后,会自动进入就绪状态
-调用join()前,主线程与t1,t2 独立运行
-为t1和t2均调用join()后,t1和t2的执行会阻塞main线程,但t1和t2不会互相阻塞
3.2 detach()方法
detach()与join()方法区别可见
https://stackoverflow.com/questions/22803600/when-should-i-use-stdthreaddetach
http://blog.csdn.net/joeblackzqq/article/details/42167617
detach()与join()的区别
-线程是否block调用主体。
-其次,join()提供了一种最直观的方法来保证线程的完成,而使用detach()方法需要额外提供方法来保证线程的完成
-两个方法是互斥使用的,使用任一方法后再使用另一方法会报异常。(使用join()后,线程已经执行完成,可能已被析构;使用detach()后,线程已经是non-joinable)
-线程创建后必须使用join或者detach,这两个方法可以保证调用线程的析构函数不会出现异常。否则程序结束(比如main返回)本地线程栈可能会被损坏,出现crash或者kill现象,导致出现一些异常:资源锁死,写到一半的文件=====
在std::thread的析构函数中,std::terminate会被调用如果:
-线程没有被Joined(用t.join())
-线程也没有被detached(用t.detach())
3.2 swap()方法
int Num = 100;
mutex m;
void thread_task1() {
for (int i = 0; i < 5; ++i) {
m.lock();
cout << "Thread_Task1: " << i << " Num is" << Num-- << endl;
m.unlock();
Sleep(100);
}
for (int i = 0; i < 10000; ++i) Sleep(10);
}
void thread_task2() {
for (int i = 0; i < 5; ++i) {
m.lock();
cout << "Thread_Task2: " << i << " Num is" << Num-- << endl;
m.unlock();
Sleep(100);
}
}
void main() {
thread t1 = thread(thread_task1);
thread t2 = thread(thread_task2);
cout << endl << endl << "Involve detach()" << endl << endl;
t1.detach();
t2.detach();
thread t3 = thread([] {cout << "t3:Lambda creates thread"; });
m.lock();
cout << endl << "before swap" << endl;
cout << "t1 thread id is " << t1.get_id() <<" t1.joinable() is " << t1.joinable() << endl;
cout << "t3 thread id is " << t3.get_id() <<" t3.joinable() is " << t3.joinable() << endl;
t1.swap(t3);
cout << endl << "after swap" << endl;
cout << "t1 thread id is " << t1.get_id() << " t1.joinable() is " << t1.joinable() << endl;
cout << "t3 thread id is " << t3.get_id() << " t3.joinable() is " << t3.joinable() << endl;
m.unlock();
cout << endl <<endl << "Join the t1" << endl << endl;
t1.join();
for (int i = 0; i < 5; ++i) {
cout << "Main is working" << endl;
Sleep(100);
}
Sleep(1000);
system("pause");
}
运行结果
-在这里使用swap方法交换了t1和t3的状态(包括id和joinable())
-t1可以再一次调用join方法(并且没有报异常),但是注意到join()方法并没有起作用。t3方法没有调用join()或detach(),并且结束后没有报异常(但是t3的这种方法是不安全的)
-使用mutex进行了互斥访问
-使用lamda创建了thread实例
此外,我们注意到thread id为0的情况,在这里0是默认线程id
当一个线程类实例的 get_id() 等于默认值的时候,即 get_id() == thread::id(),表示这个线程类实例处于下述状态之一:
-尚未指定运行的任务(thread t;)
-线程运行完毕
-线程已经被转移 (move) 到另外一个线程类实例
-线程已经被分离 (detached)
3.3 move方法
void main() {
thread t1 = thread(thread_task1);
thread t2 = thread(thread_task2);
t1.join();
t2.join();
thread t3 = thread([](int n) {
cout << "t3:Lambda creates thread";
Sleep(n);
cout << "Wake" << endl;
},10000);
//t1 = move(t3);//will lead to std::terminate()
t3.detach();
t1 = move(t3);//correct
for (int i = 0; i < 5; ++i) {
cout << "Main is working" << endl;
Sleep(100);
}
Sleep(1000);
system("pause");
}
move()方法的特点
-move()方法和swap()方法的区别是move()方法同时还会转移thread的实例内容
-t1 = move(t3)后,t3不在代表任何thread,在这之前若t3或者t1是joinable(),则将调用terminate()。
-执行move后,若原t1线程内实例未执行完成,则将继续执行,但是无法通过t1访问原线程