编程经验点滴(四)——《使用类的成员函数作为线程的执行函数》


编程经验点滴(四)


使用类的成员函数作为线程的执行函数

2005-03-29


使用类的成员函数作为线程的执行函数

2005-03-29


使用类的成员函数作为线程的执行函数

2005-03-29

我目前所做的项目是在Linux下的,项目中的某一部分需要在一个类中有“守护”线程,检查类中某些部分的状态并做出相应反映,下面介绍这部分的思路。

1、  线程相关

首先在这里介绍一点关于线程的只是,但是不详细介绍,如果需要详细了解请参阅POSIX线程详解》

 

 

 

l         pthread_create()函数

【声明】

         #include <pthread.h>

         int pthread_create(pthread_t *thread,

                            pthread_attr_t *attr,

                            void *(*start_routine)(void *),

                            void *arg );

 

 

 

【参数】

thread        pthread_t类型的指针。pthread_t类型在pthread.h中定义,通常称为线程 id(缩写为 "tid")。可以认为它是一种线程句柄;

attr          用来定义线程的某些属性。一般只需将该参数设为 NULL,使用缺省属性即可;

start_routine 新线程启动时调用的函数名。调用的函数把void * 作为参数,同时返回值的类型也是 void *,这表明可以用 void * 向新线程传递任意类型的数据,新线程完成时也可返回任意类型的数据;

arg           向线程所调用的函数中传递参数的参数。

 

 

 

【返回值】

0 - 成功创建线程;

0 - 创建失败。如:

EAGAIN errno.h中定义,#define EAGAIN 11系统没有足够的资源来新建线程(有可能是你的线程数量已经超出最大线程数量,这个数量在由PTHREAD_THREADS_MAX这个宏定义指出);

ENOMEM error.h中定义,“#define ENOMEM 12”内存不足。

 

 

 

l         pthread_cancel()

【声明】

         #include <pthread.h>

         int pthread_cancel(pthread_t thread);

 

 

 

【参数】

thread        - 待取消的目标线程,函数向thread所指向的线程发送cancel请求。

 

 

 

【返回值】

0 - 执行成功;

0 - 执行失败。如:

ESRCH 没有找到pthread所指向的线程

 

 

 

2、  我的问题

上面介绍了线程的相关知识,下面按照我当时的思考过程逐步介绍,希望能够对大家有所启发。

我所做的项目需要完成这样的功能,每个实例有自己的独立的空间,“守护线程”处理类内部的事物。它们之间又有数据交互,接到数据后进行相应的处理再发送出去。类有接收数据的缓冲区,防止处理不及时,“守护线程”发现缓冲区里面有数据则处理,没有则空转。

首先分析问题,我们需要建立一个类,类中有线程变量、线程所要执行的函数以及其它一些相关的信息,类声明如下:

             

          #include <pthread.h>

          #include <error.h>

         

              class Device

          {

          private:

              pthread_t m_thread;

              void *tfun(void *arg);

              void Destroy(void);

          public:

              Device(args);

               ~Device();

               int Init(args);

               ......

          };

【说明】

1)声明中的"args"代表参数若干,下同。

2)这个工程没有采用异常机制,根据返回值来确定函数执行情况。构造函数没有返回值,不能反映运行的状态,而建立线程有可能产生错误,所以引入Init()函数。把不会产生错误的操作(如赋初值等)放在构造函数里面,把所有可能产生错误的操作都放在这个函数里面,这样就可以得知程序的初始化结果。

 

 

 

到了这一步,我理所当然的写下了如下的实现语句:

 

 

 

                   int Device::Init(args)

         {

              int errNo = 0;

        

              errNo = pthread(&m_thread, NULL, tfun, NULL);

              if ( errNo )

              {

                   goto END;

              }

              ......

         END:

              Destroy();

              return errNo;

         }

【说明】

1)现在基本不提倡使用goto语句,但是我觉得可以单向使用goto语句,这样即不会跳来跳去的使人迷惑,又可以提高效率,亦可以使函数能够具有“单入口单出口”(只有一个return语句,而不是每个错误处直接用return返回错误值);

2)也许高手一眼就看出来这里有问题了,但是我当时没有看出来,编译后提示:

              main.cpp: In member function `int Device::Init(int)':

              main.cpp:XX: no matches converting function `tfun' to type `void*(*)(void*)'

              main.cpp:XX: candidates are: void* Device::tfun(void*)

当时想了许久,参数改了又改,还是编译不过,后来才知道,非静态成员函数不能作为线程的执行函数!看来得改思路了,想了一下有两种方法可行:第一、把成员函数声明为静态函数;第二、另外写一个非成员函数void *Fun(viod *arg)作为线程的执行函数。但是这两中方法都有自己的问题。第一种方法,静态函数不能访问非静态变量,虽然他是类的成员函数,但是访问不到所有变量。第二种方法,需要将实例的指针传给函数,但是函数又只有访问public变量的权限。

对于第一种方案,我们需要进行如下调整:(1)函数声明为静态函数;(2)在调用函数tFun()的时候将本实例的指针thisvoid *的形式传递进去;(3)在函数tFun()的内部增加指针转化语句“Device *mthis = (Device *)arg;”。

对于第二种方案,我们需要进行如下调整:(1)将函数声明为类Device的友元函数(否则访问不到非public成员变量);(2)在调用函数Fun()的时候将本实例的指针thisvoid *的形式传递进去;(3)在函数Fun()的内部增加指针转化语句“Device *mthis = (Device *)arg;”。

想比较而言,我认为第一种方案结构比较紧凑,使用起来也比较方便,遂整理代码如下:

 

 

 

         #include <pthread.h>

         #include <error.h>

        

         class Device

         {

         private:

              void Destroy(void);

        

              pthread_t m_thread;

              static void *tfun(void *arg);

         public:

              Device(void);

              ~Device();

        

              int Init(int args);

         };

        

         int Device::Init(int args)

         {

              int errNo = 0;

        

              errNo = pthread_create(&m_thread, NULL, tfun, this);

              if ( errNo )

              {

                   goto END;

              }

        

              ......

         END:

              return errNo;

         }

        

         void *Device::tfun(void *arg)

         {

              Device *mthis = (Device *)arg;

              ......

         }

至此,问题得到解决。

上一篇:编程经验点滴(三)——《C、C++中指针加 1 的问题(2004-11-23)》
下一篇:

声明:原创,版权所有,如需转载请注明出处。
http://blog.csdn.net/blankman/archive/2005/03/29/333031.aspx

PS:现在csdn的编辑功能越来越难用了 -_-!! 调不动了,以后做个模板再说吧
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值