C++11多线程 - Part 2: 连接和分离线程

翻译:https://thispointer.com//c11-multithreading-part-2-joining-and-detaching-threads/

本篇讨论的是std::thread的连接和分离

使用std:🧵:join()连接线程

一旦一个线程启动,那么另一个线程需要等待这个新线程完成。针对这种情况,另一个线程需要对std::thread对象调用join()函数。即:

std::thread th(funcPtr);
// Some Code
th.join();

让我们看一个例子:
假设主线程必须启动10个工作线程,并且在启动所有这些线程之后,主函数将等待它们完成。连接所有线程后,主函数将继续

#include <iostream>
#include <thread>
#include <algorithm>
#include <vector>

class WorkerThread
{
public:
	void operator()()
	{
		std::cout<<"Worker Thread "<<std::this_thread::get_id()<<" is Executing"<<std::endl;
	}
};
int main()
{
	std::vector<std::thread> threadList;
	for(int i = 0; i < 10; i++)
	{
		threadList.push_back( std::thread( WorkerThread() ) );
	}
	// Now wait for all the worker thread to finish i.e.
	// Call join() function on each of the std::thread object
	std::cout<<"wait for all the worker thread to finish"<<std::endl;
	std::for_each(threadList.begin(),threadList.end(), std::mem_fn(&std::thread::join));
	std::cout<<"Exiting from Main Thread"<<std::endl;
	return 0;
}

使用std:🧵:detach()分离线程

分离的线程也叫daemon / Background线程。为了分离一个线程,我们需要对std::thread对象调用std::detach()函数,即:

std::thread th(funcPtr);
th.detach();

在调用detach()后,std::thread对象不再与实际执行的线程相关联。

在线程句柄上要小心调用detach() and join()

Case 1: 永远不要在没有相关联的执行线程 std::thread 对象上调用 join()或detach()

std::thread threadObj( (WorkerThread()) );
threadObj.join();
threadObj.join(); // It will cause Program to Terminate

当对线程对象调用join()函数时,则当此join()返回时,则std::thread对象没有与其关联的线程。如果对此类对象再次调用join()函数,则会导致程序终止。

同样地,调用detach()函数使std::thread对象不在与任意线程函数连接时。在这种情况下再次对std::thread对象调用detach()函数将会造成程序终止。

std::thread threadObj( (WorkerThread()) );
threadObj.detach();
threadObj.detach(); // It will cause Program to Terminate

因此,每次在调用join()或detach()函数之前,我们都应该检查一下线程是否是连接的

std::thread threadObj( (WorkerThread()) );
if(threadObj.joinable())
{
	std::cout<<"可以使用detach, Detaching Thread "<<std::endl;
	threadObj.detach();
}
if(threadObj.joinable())
{
	std::cout<<"可以使用detach, Detaching Thread "<<std::endl;
	threadObj.detach();
}

std::thread threadObj2( (WorkerThread()) );
if(threadObj2.joinable())
{
	std::cout<<"可以使用join, Joining Thread "<<std::endl;
	threadObj2.join();
}
if(threadObj2.joinable())  
{
	std::cout<<"可以使用join,  Joining Thread "<<std::endl;
	threadObj2.join();
}

Case 2 : 永远不要忘记对相关联的执行线程std::thread对象调用join或detach

#include <iostream>
#include <thread>
#include <algorithm>
class WorkerThread
{
public:
	void operator()()
	{
		std::cout<<"Worker Thread "<<std::endl;
	}
};
int main()
{
	std::thread threadObj( (WorkerThread()) );
	// Program will terminate as we have't called either join or detach with the std::thread object.
	// Hence std::thread's object destructor will terminate the program
	return 0;
}

//结果:Abort trap: 6

同样地,我们不应该忘记在异常情况下调用join()或detach()。为了防止与我们应该使用资源获取即初始化(RAII)。

#include <iostream>
#include <thread>
class ThreadRAII
{
	std::thread & m_thread;
public:
	ThreadRAII(std::thread & threadObj) : m_thread(threadObj)
	{
	}
	~ThreadRAII()
	{
		// Check if thread is joinable then detach the thread
		if(m_thread.joinable())
		{
			m_thread.detach();
		}
	}
};
void thread_function()
{
	for(int i = 0; i < 10000; i++)
		std::cout<<"thread_function Executing"<<std::endl;
}

int main()
{
	std::thread threadObj(thread_function);

	// If we comment this Line, then program will crash
	ThreadRAII wrapperObj(threadObj);
	return 0;
}

附加: join() , detach(), joinable()

进程是一个程序的实例,一个进程可能包含多个线程,一个进程包含一个主线程
C++中,线程的作用在于执行代码、调用函数,主线程的入口即是main(),因此所有代码都是从main开始的
我们可以创建子线程,让主线程和子线程同时去干事情,例子:

#include <iostream>
#include <thread>

void thread_function()
{
    for(int i = 0; i < 100; i++)
        std::cout<<"thread function Executing"<<std::endl;
}

int main()
{
    std::thread threadObj(thread_function);
    for(int i = 0; i < 100; i++)
        std::cout<<"Display From MainThread"<<std::endl;
    threadObj.join();
    std::cout<<"Exit of Main function"<<std::endl;
    return 0;
}

/*输出结果
Display From MainThread
...
thread function Executing
...
Display From MainThread
...
thread function Executing
...
Exit of Main function
*/

从结果可以看到,子线程和主线程的调度是由操作系统决定的,所以输出结果可能是交错的

join()作用

当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续
即:当在主线程中执行到threadObj.join()方法时,就认为主线程应该把执行权让给threadObj

detach()作用

将当前线程与子线程threadObj分离,子线程threadObj交由守护线程管理,与当前线程无关
即当前线程与子线程之间没有联系了

threadObj.detach()表示该线程在后台允许,无需等待该线程完成,继续执行后面的语句。

joinable()作用

返回子线程threadObj能否join/detach
一个子线程join了之后就不能再detach,detach之后不能再join

在下面这些情况下一个线程对象是不可连接的:

  • 如果线程是默认构造的(例如std::thread foo;)
  • if it has been moved from (either constructing another thread object, or assigning to it).
  • 如果调用了其成员函数join或detach。
    例如:
#include <iostream>
#include <thread>

void mythread()
{

}

int main()
{
	std::thread foo;
	std::thread bar(mythread);

	std::cout << "Joinable after construction:\n" << std::boolalpha;
	std::cout << "foo: " << foo.joinable() << '\n';
	std::cout << "bar: " << bar.joinable() << '\n';

	if (foo.joinable()) foo.join();
	if (bar.joinable()) bar.join();

	std::cout << "Joinable after joining:\n" << std::boolalpha;
	std::cout << "foo: " << foo.joinable() << '\n';
	std::cout << "bar: " << bar.joinable() << '\n';

	return 0;
}

/*输出结果
Joinable after construction:
foo: false
bar: true
Joinable after joining:
foo: false
bar: false
*/
posted on 2020-04-15 16:14  JJ_S  阅读(1209)  评论(0编辑  收藏  举报