1. 基本线程管理
所有的C++程序都至少包含一个线程(main函数所在的C++运行时线程),程序在启动主线程之后可以创建其他的线程,这些线程使用它们的初始化函数作为程序的起点(如(一)中的void hello函数),当创建线程之后我们需要启动创建的这些新的线程。
2. 线程的启动
C++线程启动的方式需要使用std::thread类来进行,大致方式如下:
使用一个函数(具体来说是可调用对象)来初始化std::thread线程对象
std::thread定义在头文件<thread>
之中
void Foo();
std::thread myThread(Foo());
除了这种最常用的方式之外还有其他几种方法来初始化std::thread
- 使用仿函数来实现:
class Task {
public:
void operator() const {
foobar();
}
};
Task t;
std::thread myThread(t);
在使用这种方式的时候需要防止产生C++中所谓的“Most vexing parse”错误,具体来说是以下一种错误:
当我们声明:std::thread myThread(Task());
我们期望的结果是:定义一个std::thread类型的变量myThread,它使用类Task变量来初始化。
实际的结果是:声明了一个函数,函数名是myThread,返回值是std::thread,形参是一个函数指针(返回值是Task对象,形参是空)
解决方式:(1)使用C++11中的初始化语法:std::thread myThread{Task()};
(2) 使用额外的一对括号:std::thread myThread ((Task()));
2. 使用C++11中的lambda表达式
std::thread myThread([](){foobar();});
3. 线程所有权的转移
std::thread线程支持所谓的“移动语义”,因此可以将std::thread进行移动,这种感觉好像是在“转移std::thread所对应的资源”,例如:
void foo();
void bar();
std::thread t1(foo);
std::thread t2 = std::move(t1) //将t1转移到t2
t1 = std::thread(bar);
这样的移动方式也可以在函数返回值中体现,也就是我们可以写出如下的代码:
std::thread f() {
void doSomething();
return std::thread(doSomething);
}
std::thread g() {
void doSomething(int);
std::thread t(doSomething, 3);
return t;
}
std::thread支持移动,可以让线程对象存储在std::vector之中,c++11中对std::vector进行了改动,它可以存储支持“移动语义”的对象。下面的程序演示了创建若干个线程,并等待它们运行结束:
void doWork(unsigned id);
void f() {
std::vector<std::thread> threads;
for (unsigned i = 0; i < 20; ++i) {
threads.push_back(std::thread(doWork(i)); //创建多个线程对象
}
std::for_each(threads.begin(), threads.end(),
std::mem_fn(&std::thread::join)); //调用它们的join方法
}
4. 线程ID
线程ID是一个std::thread::id类型的变量,可以通过get_id获取ID值。如果一个线程对象没有一个与之关联正在执行的线程,那么get_id返回一个std::thread::id默认值对象,表示没有任何线程。另一种方式是使用std::this_thread::get_id来获取当前线程的ID。