C++并发实战4:thread object is movable,not copyable

原创 2013年12月03日 17:56:57

       假设这样一个情形:假设函数A创建一个thread object返回给A的调用者,或者A将thread obejct转移给其它函数。thread object is movable but no copyable like std::unique_ptr or std::ifstream可以看出thread object是可以转移其所有权的。

void some_function();
void some_other_function();
std::thread t1(some_function); //创建thread object t1运行some_function()
std::thread t2=std::move(t1); //采用std::move移动语义将thread object t1移动到t2,此时t2接管运行some_function()的线程,而t1此时没有执行线程和其关联
t1=std::thread(some_other_function); //此处是rigth-value语义,先创建一个临时thread object执行some_other_function(),然后将临时的thread object赋值给t1
std::thread t3; //采用默认的thread构造方式创建,没有任何线程执行体和t3相关
t3=std::move(t2); //t2没有线程执行体了,t3关联的线程执行some_other_function()
t1=std::move(t3); //t1正在执行some_function()而t3赋值给t1,会导致std::terminated()会停止t1关联的线程,然后将some_other_function()线程执行体和t1关联,t3没有关联线程
         题外篇:注:在C++11中,标准库在<utility>中提供了一个有用的函数std::move,这个函数的名字具有迷惑性,因为实际上std::move并不能移动任何东西,它唯一的功能是将一个左值强制转化为右值引用,继而我们可以通过右值引用使用该值,以用于移动语义。从实现上讲,std::move基本等同于一个类型转换:static_cast<T&&>(lvalue); 值得一提的是,被转化的左值,其生命期并没有随着左右值的转化而改变。如果期望std::move转化的左值变量lvalue能立即被析构,那么肯定会失望了。

#include<iostream>
#include<utility>
using namespace std;
class test{
    public:
        test(int i=0):data(i){}
        test(const test& one){
            cout<<"copy constructor"<<endl;
            data=one.data;
        }
        test& operator=(const test& one){
            cout<<"operator="<<endl;
            data=one.data;
            return *this;
        }
        ~test(){
            cout<<"deconstructor"<<endl;
        }
    public:
        int data;
};
int main(){
    test one(10);
    cout<<"one "<<one.data<<endl;
    test two(std::move(one));
    cout<<"tow "<<two.data<<endl;
    return 0;
}

程序输出:

one 10
copy constructor          //std::move会调用拷贝构造函数
tow 10
deconstructor   
deconstructor      //出现两个析构函数,证明std::move没有移动任何东西,只是转换而已


1  thread object作为函数返回值,函数参数

#include<thread>
#include<iostream>
#include<utility>
using namespace std;
void fun(){
    cout<<"thread fun()"<<endl;
}
thread f(thread t){//thread object作为函数参数和返回值
    return t;
}
void g(){
    thread t(fun);
    //thread g=f(t);//thread(thread&) = delete;禁止拷贝构造,所以采用move语义
    thread g=f(move(t));
    if(g.joinable()){//检测joinable是有必要的
        cout<<"yes"<<endl;
        g.join();
    }
}
int main(){
    g();
    return 0;
}

   说明:由于升级了g++,不再使用boost::thread了,假设上面程序保存为test.cpp则编译:g++ -o test test.cpp -std=c++11 -lpthread

 程序输出:

yesthread fun()


2   thread object可以转移所有权一个用处就是,采用RAII手法用一个栈对象thread_guard可以保护thread object。当一个thread object创建后,thread_guard构造时将拿到thread的所有权,然后在thread_guard析构的时候调用thread::join()等待线程完成,且thread_object是禁止拷贝和复制的。这样一来没有其它对象或函数将无法再获得thread object的所有权,保证了thread_gurad的销毁时线程已经完成任务,不会发生超出thread_guard后仍引用thread的情形。可以用一个scoped_thread来描述thread_guar。这样的手法在mutex和mutex_guard互斥量的保护中也是同样的道理。

        scoped_thread采用引用保护机制的例子:

#include<iostream>
#include<thread>
#include<boost/noncopyable.hpp>
#include<unistd.h>
using namespace std;
class thread_gurad:boost::noncopyable{
    public:
        explicit thread_gurad(thread& t):t_(t)
        {
            if(!t_.joinable())
                cout<<"t_ can't joinable"<<endl;
            cout<<"thread_gurad"<<endl;
        }
        ~thread_gurad(){
            t_.join();
            cout<<"~thread_gurad"<<endl;
        }
    private:
        thread& t_;
};
void fun(){
    sleep(1);
}
int main(){
    thread t(fun);
    thread_gurad guard(t);
    return 0;
}
程序输出:

thread_gurad
~thread_gurad

          

   scoped_thread采用std::move()语义:

#include<thread>
#include<utility>
#include<stdexcept>
#include<stdio.h>
#include<unistd.h>
//#include<boost/thread.hpp>
using namespace std;
class scoped_thread{
    public:
        explicit scoped_thread(std::thread t)
            :t_(std::move(t))
        {
            printf("scoped_thread\n");
            if(!t_.joinable())
                throw std::logic_error("No thread");
        }
        ~scoped_thread()
        {
            t_.join();
            printf("~scoped_thread\n");
        }
        scoped_thread(const scoped_thread& )=delete;
        scoped_thread& operator=(const scoped_thread&)=delete;
    private:
        std::thread t_;
};
void do_something_in_current_thread(){}
class func{
    public:
        func(int i=0):data(i){}
        void operator()(){
            sleep(1);
        }
    public:
        int data;
};
void f(){
    int local=0;
    func my_fun(local);
    //scoped_thread t(thread(my_fun));//这样是错误的
    thread myThread(my_fun);
    scoped_thread t(move(myThread));
    printf("f() exit\n");
}
int main(){
    f();
    return 0;
}


程序输出:

scoped_thread
f() exit
~scoped_thread

3   由于thread object is movable,所以可以将其作为容器元素,如vector经常插入删除操作涉及到移动元素。

#include<iostream>
#include<thread>
#include<functional>
#include<algorithm>
#include<vector>
#include<stdio.h>
using namespace std;
class fun{
    public:
        explicit fun(int i=0):data(i){}
        void operator()(){
            printf("%d\n",data);//cout会出现乱序
        }
    private:
        int data;
};
int main(){
    std::vector<thread> threads;
    for(int i=0;i<10;i++)
        threads.push_back(thread(fun(i)));//
    for_each(threads.begin(),threads.end(),mem_fn(&thread::join));//std::mem_fn这里将一个类成员函数转换为普通函数来使用
    return 0;
}

程序输出1到10数字,每个数字占一行





相关文章推荐

Linux下编译C/C++以及gcc/g++常用参数

编译的步骤gcc and g++分别是c & c++编译器 。gcc/g++在执行编译工作的时候,总共需要4步1.预处理,生成.i的文件[预处理器cpp]。 对应的参数是 -E 2.将预处理后的文...
  • hsd2012
  • hsd2012
  • 2016年04月19日 16:07
  • 1273

C++11闭包函数:Lambda表达式的使用λ

C++11中的Lambda表达式用于定义并创建匿名的函数对象,以简化编程工作。Lambda的语法形式如下: [函数对象参数] (操作符重载函数参数) mutable或exception声明 -> 返...

详解log4j:ERROR A "org.apache.log4j.ConsoleAppender" object is not assignable to a ...

在Maven项目中,执行mvn compile时,会调用maven-compiler-plugin插件,执行具体的源代码编译。如果Maven项目中有对log4j的依赖设置,则中compile阶段会出现...

实战c++中的智能指针unique_ptr系列-- 使用std::unique_ptr代替new operator(错误:‘unique_ptr’ is not a member of ‘std’)

写了很多篇关于vector的博客,其实vector很便捷,也很简单。但是很多易错的问题都是vector中的元素为智能指针所引起的。所以决定开始写一写关于智能指针的故事,尤其是unique_ptr指针的...

Could not allocate space for object because the PRIMARY filegroup is full

SQL Server DBA Diaries   Could not allocate space for object because the PRIMARY filegroup...

Dbflow:Model object: xxxx is not registered with a Database. Did you forge an annotation?

错误代码: 错误描述,在三星、小米手机上运行没问题,在华为手机上抛出这个异常。 网上查询没有找到原因。 官网上提示说没有在application初始化的情况会抛出这个异常,但是初始化以...

java axis调用cxf 的坑object is not an instance of declaring class while invoking public

简单说一下出错的场景,项目需要为别人系统开放webservice,于是在springmvc框架下集成cxf开发webservice接口,网上教程一大堆,小有波折,参考的 https://my.osc...

python错误TypeError: 'module' object is not callable 的解决方法

转载自:http://blog.csdn.net/huzhenwei/article/details/2895909 问题:             我不能确定我为什么得到这个错误:...

Incorrect decrement of the reference count of an object that is not owned at this point by the calle

Incorrect decrement of the reference count of an object that is not owned at this point by the cal...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C++并发实战4:thread object is movable,not copyable
举报原因:
原因补充:

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