C++并发实战2:thread::join和thread::detach

原创 2013年12月02日 14:11:29

    thread::join()是个简单暴力的方法,主线程等待子进程期间什么都不能做,一般情形是主线程创建thread object后做自己的工作而不是简单停留在join上。thread::join()还会清理子线程相关的内存空间,此后thread object将不再和这个子线程相关了,即thread object不再joinable了,所以join对于一个子线程来说只可以被调用一次,为了实现更精细的线程等待机制,可以使用条件变量等机制。

      异常环境下join,假设主线程在一个函数f()里面创建thread object,接着f()又调用其它函数g(),那么确保在g()以任何方式下退出主线程都能join子线程。如:若g()通过异常退出,那么f()需要捕捉异常后join

#include<iostream>
#include<boost/thread.hpp>
void do_something(int& i){
    i++;
}
class func{
    public:
        func(int& i):i_(i){}
        void operator() (){
            for(int j=0;j<100;j++)
                do_something(i_);
        }
    public:
        int& i_;
};
void do_something_in_current_thread(){}
void f(){
    int local=0;
    func my_func(local);
    boost::thread t(my_func);
    try{
        do_something_in_current_thread();
    }
    catch(...){
        t.join();//确保在异常条件下join子线程
        throw;
    }
    t.join();
}
int main(){
    f();
    return 0;
}


      上面的方法看起来笨重,有个解决办法是采用RAII(资源获取即初始化),将一个thread object通过栈对象A管理,在栈对象A析构时调用thread::join.按照局部对象析构是构造的逆序,栈对象A析构完成后再析构thread object。如下:

#include<iostream>
#include<boost/noncopyable.hpp>
#include<boost/thread.hpp>
using namespace std;
class thread_guard:boost::noncopyable{
    public:
        explicit thread_guard(boost::thread& t):t_(t){}
        ~thread_guard(){
            if(t_.joinable()){//检测是很有必要的,因为thread::join只能调用一次,要防止其它地方意外join了
               t_.join();
            }
        }
        //thread_guard(const thread_guard&)=delete;//c++11中这样声明表示禁用copy constructor需要-std=c++0x支持,这里采用boost::noncopyable已经禁止了拷贝和复制
        //thread_guard& operator=(const thread_guard&)=delete;
    private:
        boost::thread& t_;
};
void do_something(int& i){
    i++;
}
class func{
    public:
        func(int& i):i_(i){}
        void operator()(){
            for(int j=0;j<100;j++)
                do_something(i_);
        }
    public:
        int& i_;
};
void do_something_in_current_thread(){}
void fun(){
    int local=0;
    func my_func(local);
    boost::thread t(my_func);
    thread_guard g(t);
    do_something_in_current_thread();
}
int main(){
    fun();
    return 0;
}

    说明:禁止拷贝和复制的原因是防止栈对象thread_guard在超出了thread object对象生命期的地方使用。如果detach一个线程则没有上述这么麻烦,必经detach后就不管子线程了。


     thread::detach()后:没有直接方法与线程通信,不可能wait了,不可能有任何thread object指向这个线程。线程变成了后台进程(孤儿进程),在Linux将由init接管,在c++中由库接管。若不确定一个线程是否有thread object指向它,那么请先用thread::joinable()检测后再thread::detach()就想前面代码中的joinable检测一样。

      考虑一个情形:通常我们在word中编辑文件A时,点击"新建"按钮后会出现新的窗口继续编辑文件B。这里A和B是相对独立的,A"新建"相当于开启一个线程去供B使用,其后A马上detach这个线程。代码如下:

#include<iostream>
#include<boost/thread.hpp>
void open_document_and_display_gui(const std::string& filename){}//“新建”这一操作
bool done_eaditing(){//是否完成编辑
    return true;
}
enum command_type{open_new_document};//用户命令,这里只有一个就是“新建”
class user_command{//用户命令封装
    public:
        user_command():type(open_new_document){}
    public:
        command_type type;
};
user_command get_user_input(){//获取用户命令,这里就是获取"新建“
    return user_command();
}
std::string get_filename_from_user(){
    return "foo.doc";
}
void process_user_input(const user_command& cmd){}//处理用户的普通输入,即文档编写

void edit_document(const std::string& filename){//假设初始时文档A执行edit_document,发现用户有”新建“命令到达,则开启一个线程去供文档B使用,且立即detach这个线程
    open_document_and_display_gui(filename);
    while(!done_eaditing()){
        user_command cmd=get_user_input();
        if(cmd.type==open_new_document){
            const std::string new_name=get_filename_from_user();
            boost::thread t(edit_document,new_name);//文档B也是采用同样的线程函数,这里可以看出thread可以接收参数,还有一个方法参考pthread_create将参数封装在一个函数对象中,然后传给thread
            t.detach();//立即执行detach,这里可以肯定thread t和线程相关,故不需要检测joinable
        }
        else
            process_user_input(cmd);//普通文档编辑
    }
}
int main(){
    edit_document("bar.doc");
    return 0;
}



相关文章推荐

thread中join和detach的区别

C++中的thread对象通常来说表达了执行的线程(thread of execution),这是一个OS或者平台的概念。 当thread::join()函数被调用,调用它的线程会被block,直到线...

线程的join 和detach状态

在任何一个时间点上,线程是可结合的(joinable),或者是分离的(detached)。一个可结合的线程能够被其他线程收回其资源和杀死;在被其他线程回收之前,它的存储器资源(如栈)是不释放的。相反,...

C++11 thread::detach(2)

原文地址:http://www.cplusplus.com/reference/thread/thread/detach/ public member function std::...

c++11特性之std::thread--初识

C++11中已经拥有了一个更好用的用于线程操作的类std::thread。默认构造函数: thread() noexcept; 构造一个任何线程不执行的线程对象。初始化函数: template ...

C++11并发之std::thread

std::thread 在 #include 头文件中声明,因此使用 std::thread 时需要包含 #include 头文件。...

boost::thread线程管理

6.1. 概述 线程就是,在同一程序同一时间内允许执行不同函数的离散处理队列。 这使得一个长时间去进行某种特殊运算的函数在执行时不阻碍其他的函数变得十分重要。 线程实际上允许同时执行两种函数,而...

解决boost::thread::join() 的阻塞

static void onRun() { for(;;) { //do sth } } boost::thread th(&onRun); th.timed_join(boost::...
  • zhylei
  • zhylei
  • 2012年10月14日 21:57
  • 5582

C++11 thread::join(4)

原文地址:http://www.cplusplus.com/reference/thread/thread/join/ public member function s...

thread 中join() 的用法

前段时间参加面试时,被一个面试官问到thread中join方法用法,那会自己答得有点唐突吧,面试官对我的回答好像不是很满意! 最近有点小空闲,就稍微看了一些书,然后自己整理了一下线程中几个方法的用法:...

之前介绍 thread join和detach的区别但是不详细 (详细介绍)

线程状态: 在一个线程的生存期内,可以在多种状态之间转换,不同的操作系统可以实现不同的线程模型,定义许多不同的线程状态,每个状态还可以包含多个子状态,但大体来说,如下几种状态是通用的: 1...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C++并发实战2:thread::join和thread::detach
举报原因:
原因补充:

(最多只允许输入30个字)