1.一般方法
一般情况下,使用线程,是用pthread_create函数创建一个线程,创建的过程中采用传递函数指针的方式来实现主体的业务逻辑。比如
/*thread.c*/
#include <stdio.h>
#include <pthread.h>
/*线程一*/
void thread_1(void)
{
int i=0;
for(i=0;i<=6;i++)
{
printf("This is a pthread_1.\n");
if(i==2)
pthread_exit(0);
sleep(1);
}
}
/*线程二*/
void thread_2(void)
{
int i;
for(i=0;i<3;i++)
printf("This is a pthread_2.\n");
pthread_exit(0);
}
int main(void)
{
pthread_t id_1,id_2;
int i,ret;
/*创建线程一*/
ret=pthread_create(&id_1,NULL,(void *) thread_1,NULL);
if(ret!=0)
{
printf("Create pthread error!\n");
return -1;
}
/*创建线程二*/
ret=pthread_create(&id_2,NULL,(void *) thread_2,NULL);
if(ret!=0)
{
printf("Create pthread error!\n");
return -1;
}
/*等待线程结束*/
pthread_join(id_1,NULL);
pthread_join(id_2,NULL);
return 0;
}
在这以上这段代码中,首先实现了业务函数treahd_1等函数,再采用pthread_create函数创建线程,把函数指针传入,以便线程创建后调用函数。那么,在业务函数执行后,我们怎么判断线程有没有执行完呢(因为有时候,我们需要等待线程执行完后才能执行下一步的逻辑),通过函数ptread_join来判断。这几个函数的具体用法,可以百度。我们这里只讨论流程。
2.面向过程的方法
在上面的过程中,我们说道了普通的方法,如果采用面向对象的方法,该如何实现呢,有些人就说了,不就是进行封装嘛。。。不要忘记了。。。当你一旦调用pthread_create函数,就会立即调用你的业务函数pthread_1函数,封装的时候,如何先不执行业务函数呢?
第一步 完成一个纯虚函数,有什么用呢,让后面的线程类继承:
class IRunnable {
public:
/**
* 析构函数
*/
virtual ~IRunnable() {}
/**
* 线程运行逻辑
*/
virtual void run() = 0;
};
第二步,声明线程类
void* esRunThread(void* target) {
try {
reinterpret_cast<es::IRunnable*>(target)->run();
} catch (es::SQLException& sqlEx) {
cerr << "Uncaught exception in Thread::run(), " << sqlEx.what() << ", "
<< sqlEx.getSQLString() << endl;
} catch (es::Exception& ex) {
cerr << "Uncaught exception in Thread::run(), " << ex.what() << endl;
} catch (...) {
cerr << "Uncaught exception in Thread::run()" << endl;
}
return 0;
}
上面的代码,是把target进行类型转换为IRunnable,然后调用其run函数。
class Thread : public IRunnable{
public:
typedef pthread_t ThreadId;
Thread();
Thread(IRunnable* target);
virtual ~Thread();
ThreadId getId() const;
void start();
void join();
/**
* 分离线程, 不允许等待已经join或detach过的线程
*/
void detach();
/**
* 休眠线程
* @param millis 休眠毫秒数
*/
static void sleep(int millis);
/**
* 暂停当前线程让其它线程先运行
*/
static void yield();
static ThreadId getCurrentThreadId();
virtual void run();
private:
IRunnable* _target;
ThreadId _id;
#endif
};
大家保持一点耐心,这里主要的函数是2个,一个 start,一个run。那么,看看这2个函数的实现
void Thread::start() {
pthread_attr_t attr;
int rc = pthread_attr_init(&attr);
if (rc) {
throw ThreadException(rc);
}
IRunnable* target = (_target ? _target : this);
rc = pthread_create(&_id, &attr, ::esRunThread, target);
if (rc) {
throw ThreadException(errno);
}
}
void Thread::run() {}
看到这里,大家看明白了吗?
这个Thread类,我们并不能直接拿来使用,为什么?因为他的run函数为空的,需要自己定义一个类,比如myThread来继承Thread,从而实现run函数。实现后,myThread.start()来启动该类。为什么start就能启动该类呢?
因为在start的过程中,创建了线程,参数当中传递的函数指针是esRunThread,函数的参数是target,而target实际上是用户自定实现的一个接口类(用来实现业务),如果没有实现该类,那么就默认为this(this实际就是用户自定义实现的线程类)。esRunThread函数把target强转后调用了run函数(用户实现的run函数),那么整个线程类的实现过程就完成了。
问大家一个小问题,如何保证让thread 不能被赋值拷贝?也就是防止pthread1=ptread2 ??