C++标准库中很多资源占有(resource-owning)类型,比如std::ifstream,std::unique_ptr还有std::thread都可以移动,但不可以拷贝
std::thread支持移动,这意味着线程的所有权可以在函数外移动,就如下面程序一样:
std::thread f()
{
void some_function();
return std::thread(some_function);
}
std::thread g()
{
void some_other_function(int);
std::thread t(some_other_function,42);
return t;
}
前面写了个thread_guard类所持有的线程是引用,移动操作就可以避免不必要的麻烦。当某个对象转移了线程的所有权后,它就不能对线程进行加入或分离。如下定义了scoped_thread类,支持移动操作:
class scoped_thread
{
std::thread t;
public:
explicit scoped_thread(std::thread t_): //1
t(std::move(t_))
{
if(!t.joinable()) //2
throw std::logic_error("No thread");
}
~scoped_thread()
{
t.join(); //3
}
scoped_thread(const scoped_thread&) = delete;
scoped_thread& operator=(const scoped_thread&) = delete;
};
struct func;
void f()
{
int some_local_state;
scoped_thread t(std::thread(func(some_local_state))); //4
do_something_in_current_thread();
}
这里新线程直接传递匿名对象到scoped_thread中(4),相当于移动操作。当主线程到达f()函数的末尾的时候,scoped_thread对象将调用析构函数(3),这里在构造函数中检查线程是否可以加入,不可以加入就抛异常。
std::thread对象的容器,如果这个容器是移动敏感的(比如vector),那么移动操作同样适用于这些容器。如下就是用于容器的量产一些线程,并且等待它们结束:
void do_work(unsigned id);
void f()
{
std::vector<std::thread> threads;
for(unsigned i = 0;i < 20; ++i)
{
threads.push_back(std::thread(do_work,i)); //产生线程
}
std::for_each(threads.begin(),threads.end(),
std::mem_fn(&std::thread::join)); //对每个线程调用join
}
将std::thread放入std::vector是向线程自动化管理迈出的第一步:并非为这些线程创建了独立的变量,并且将它们加入,可以把它们作为一个组。创建一组线程(数量在运行时确定),可使的这一步迈得更大,而非像上述那样创建固定数量的线程。