编程经验点滴(四)
使用类的成员函数作为线程的执行函数
使用类的成员函数作为线程的执行函数
使用类的成员函数作为线程的执行函数
我目前所做的项目是在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()的时候将本实例的指针this以void *的形式传递进去;(3)在函数tFun()的内部增加指针转化语句“Device *mthis = (Device *)arg;”。
对于第二种方案,我们需要进行如下调整:(1)将函数声明为类Device的友元函数(否则访问不到非public成员变量);(2)在调用函数Fun()的时候将本实例的指针this以void *的形式传递进去;(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