C++11多线程异常

       一旦开始了线程,需要显示决定要等待线程函数完成或分离它自行完成。如果detach()线程不等待,你要确保通过线程访问的数据是有效的,直至该线程完成为止,例如线程函数持有局部变量的指针或引用,且当主函数退出的时候线程未完成,就会出错,线程函数就会访问一个已被销毁的变量,解决方法是数据私有化。join()背后的含义有两层,一是等待子线程执行完毕,避免主线程先完成,从而导致子线程终止,二是join()会清理与子线程相关联的存储器,这样std::thread对象不再与子线程相关联,这就意味着你只能对一个给定线程调用一次join(),一旦调用了join(),此std::thread对象不再是可连接的,并且joinable()将返回false。

struct func
{
	int &i;
	func(int& i_):i(i_){}
	void operator()
	{
		for(unsigned int j=0;j<1000;j++)
			do_something(i);             //可能会访问悬空引用
	}
};
void main()
{
	int some_local_state=0;
	func my_func(some_local_state);
	std::thread my_thread(my_func);
	my_thread.detach();                           //不等待线程完成,可能新的线程仍在运行
}

        如果打算等待该线程,就需要仔细选择在代码那个位置调用join(),如果在线程开始调用后,join()之前引发了异常,对join()的调用就会跳过。为了避免程序在引发异常的时候被终止,还需要在存在异常时调用join()。

struct func;
void main()
{
	int some_local_state=0;
	func my_func(some_local_state);
	std::thread t(my_func);
	try
	{
		do_somthing_in_current_thread();
	}
	catch(...)
	{
		t.join();
		throw;
	}
	t.join();
}

使用try/catch块很罗嗦,而且容易将作用域弄乱,所以并不是一个理想方案。如果确保线程必须在函数退出前完成是很重要的,无论是因为它具有对其他局部变量的引用还是任何其他原因,那么确保这是所有可能退出路径的情况很重要,无论正常还是异常,希望提供一个简单明了的机制。这种做法之一是使用标准的资源获取初始化,并提供一个类,在它析构函数中进行join()。

class thread_guard
{
	std::thread t;
public:
	explicit thread_guard(std::thread &t_):t(t_){}
	~thread_guard()
	{
		if(t.joinable())                          //1
		{
			t.join();                         //2
		}
	}
	thread_guard(thread_guard const&)=delete;         //3
	thread_guard& operator=(thread_guard const&)=delete;
}
struct func;
void main()
{
	int some_local_state=0;
	func my_func(some_local_state);
	std::thread my_thread(my_func);
	thread_guard g(t);

	do_something_in_current_thread();                //4              
}

        在主线程执行到末尾4时,局部变量会按照构造函数的逆序销毁,因此g首先被销毁,并且析构函数2中线程被结合,即便4中引发异常的情况下也会发生。拷贝构造函数和拷贝赋值运算法被禁止,以确保它们不会被编译器自动生成,复制或赋值这样一个对象时比较危险的,因为它们可能比它要结合的线程的作用域存在的更久。

 
  
 
  
 
  
 
  
 
  
 
  
 
 

你可能感兴趣的:(C++)