2.3转移线程的所有权(C++并发编程实战)

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 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是向线程自动化管理迈出的第一步:并非为这些线程创建了独立的变量,并且将它们加入,可以把它们作为一个组。创建一组线程(数量在运行时确定),可使的这一步迈得更大,而非像上述那样创建固定数量的线程。

你可能感兴趣的:(C++并发编程实战)