Chinaunix首页 | 论坛 | 博客
  • 博客访问: 336322
  • 博文数量: 32
  • 博客积分: 1470
  • 博客等级: 上尉
  • 技术积分: 575
  • 用 户 组: 普通用户
  • 注册时间: 2006-05-31 11:38
个人简介

实践主义者,多行路远胜于多读书。

文章分类

分类: C/C++

2008-03-25 17:16:23

使用linux下的线程创建函数pthread_create已久,在传递给它void*型的入口参数时,总是两种方式:
1. 在堆中创建,传入参数指针至线程中,由线程内部释放或等待线程退出后再释放;
2. 不在堆中创建或使用全局变量;

虽然在各种书上提及这种方式的种种不足,但我一直用得还挺好.主要就是注意资源的释放就成,毕竟这种低层的API能给程序员最大的灵活性,所谓有空间才能发挥嘛.但不可否认的是,站得更高是会看得更远.一些设计确实能让代码变得更加简洁,并且不用关注太多低层的细节.其实我一直觉得细节很重要,但要会取舍.


看看以下几个类如何能让代码更优雅:
boost::shared_ptr
boost::bind
boost::thread


作为比较,以下是linux下标准的创建线程的例子:



/*
  build args:
  -lpthread
*/


#include <pthread.h>
#include <iostream>
#include <string>

using namespace std;

class B
{
public:
    B() {cout << "construct B;" << endl;}
    void out() { cout << "B::out();" << endl; }
    ~B(){ cout << "destroy B;" << endl;}
};

void* fun2(void* p)
{
    B* pb =(B*)p;
    pb->out();
    sleep(3);
    delete pb; // 在这里释放主线程中创建的对象.
}

void fun()
{
    pthread_attr_t ta;
    pthread_attr_init(&ta);
    pthread_t tid;

    B *pb = new B(); // 创建B的对象作为线程的参数
    pthread_create(&tid, &ta, (void*(*)(void*))fun2, (void*)pb);
    pthread_join(tid, NULL);
}

int main()
{
    fun();
    cout << "FINISH" << endl;
}



如果用上shared_ptr,就不再需要在线程中来删除资源了.因为当引用计数为0时,shared_ptr会自动的销毁掉内含的指针.



/*
  build args(boost 1.34.1 installed /usr/local/):
  -I/usr/local/include/boost-1_34_1 -L/usr/local/lib -lboost_thread-gcc41-mt
*/



#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>

#include <iostream>
#include <string>

using namespace std;

class B
{
public:
    B() {cout << "construct B;" << endl;}
    void out() { cout << "B::out();" << endl; }
    ~B(){ cout << "destroy B;" << endl;}
};

void* fun2(void* p)
{
    /*
       转呀转就把原类型转回来了.
    */

    boost::shared_ptr<B> pb = *(boost::shared_ptr<B>*)(p);
    pb->out();

    sleep(10);
    
    // 注意在这里就不再需要delete
}



void fun()
{
    /*
       boost中的引用指针计数器(shared_ptr),这个shared_ptr对象内含B的对象指针,
       但由于fun2()函数的参数是void*,所以还需要取这个对象的地址传入.
       fun2()其实可以直接用boost::shared_ptr类型,就无须这样麻烦的转换,不过这样的话,就不能支持任意类型了.
    */

    boost::shared_ptr<B> pb(new B());
        
    /*
       这里不再需要关注thread的创建细节;
       boost::bind设计得太巧妙了;
    */

    boost::thread bthread(boost::bind(fun2, (void*)&pb));

    bthread.join();
}


int main()
{
    fun();
    cout << "FINISH" << endl;
}


上面的类型转换有点恶心,不过好在shared_ptr也是允许内部指针的类型继承的,好绕口.

还是看以下代码吧,B类由A类派生而来.所以boost::shared_ptr这个类型,可以传递boost::shared_ptr对象,并且由于shared_ptr重载了"->"符号,所以直接就可以访问B的成员了.




#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <string>


using namespace std;


class A
{
public:
        virtual void out() {cout << "A::out();" << endl;}
};

class B : public A
{
public:
    B() {cout << "construct B;" << endl;}
    virtual void out() { cout << "B::out();" << endl; }

    ~B(){ cout << "destroy B;" << endl;}
};



/*
  fun2 可以接收任何A类以及从A派生的类的shared_ptr对象.
  这样做,勉强也可算是支持任意类型了,因为只要传递的参数是从A派生即可.
*/

void* fun2(boost::shared_ptr<A> pa)
{
    // 不再需要任何转换,直接访问其子类的虚方法了.

    pa->out();
    sleep(10);
}

void fun()
{
    /*
       代码确实很简洁了.
    */

    boost::shared_ptr<B> pb(new B());
    boost::thread bthread(boost::bind(fun2, pb));

    bthread.join();
}

int main()
{
    fun();
    cout << "FINISH" << endl;
}



阅读(11814) | 评论(3) | 转发(1) |
给主人留下些什么吧!~~

yulc2011-07-28 13:52:56

确实是的, 示例代码虽然没有问题. 但实际上, shared_ptr如果先于"新创建的线程"就被销毁,就会导致出错.

chinaunix网友2011-07-11 11:24:37

tntvampire 所言极是

chinaunix网友2010-06-19 02:36:35

""" void* fun2(void* p) { /* 转呀转就把原类型转回来了. */ boost::shared_ptr pb = *(boost::shared_ptr*)(p); pb->out(); sleep(10); // 注意在这里就不再需要delete } """ 博主,上面的这个函数是有问题的;我猜测您的意思是想为每个执行的线程函数持有一个类型B的ownership(即指向B类型的boost::shared_ptr),但要做到线程安全必须满足一个前提:保证在进入线程函数前完成boost::shared_ptr引用计数的自增;您这里是把boost::shared_ptr的指针传进线程函数,而boost::shared_ptr却还未进行任何关于ownership语义的操作。 以下是一种会出现问题的步骤: 1.线程a以B对象的指针构造boost::shared_ptr,获得B的ownership,B的引用计数变为1 2.线程a启动线程b,并把B的指针传