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
*/