您的位置:首页 > 编程语言 > C语言/C++

C++封装一个简单的线程类

2015-10-19 15:47 513 查看



多线程编程简介:

大家在编程时,经常需要在程序中启动一个或多个线程来处理任务,而如果每次都是去调用系统创建线程的API函数来创建,代码量虽不多,但线程的创建和业务逻辑代码就揉在一起了,且创建多个线程的时候,有大量的重复代码,不便于维护。若我们把创建线程和销毁线程的这些共同的代码封装到一个类中,这样我们可以更专注业务逻辑的实现,在其它地方直接拿来用就行,程序也便于维护和扩展。而且这样的话即使程序所使用的线程库更换了,但线程类提供的接口没变,所以我们的业务逻辑代码也不用任何的改动。

创建一个线程也无非就是调用系统线程API或第三方库的API,然后传入线程函数地址和线程运行所需要的参数即可,所以我们需要将此部分代码抽象出来,并提供使用接口即可。

一个线程基类Thread

这里我们使用的是Apache提供的apr库,该库具有跨平台性,当然不管使用什么库,我们提供的接口都是一样的,且线程创建和销毁的逻辑是一样的。代码:

1 class Thread

2 {

3 public:

4 Thread(bool bDetach = true);

5 virtual ~Thread();

6

7 virtual void run() = 0; //业务接口

8

9 int start(); //启动线程

10 int join(); //等待线程线束

11 void destroy(); //销毁线程所申请的资源

12

13 // attribute functions

14 int get_thread_id() { return thr_id_; }

15 void set_thread_id(unsigned long thrId) { thr_id_ = thrId; }

16

17 protected:

18 static void* __stdcall thread_proc(apr_thread_t* th, void* data);

19 void notify() { cond_.signal(); }

20 bool check_interrupt() { return bExit_; }

21

22 private:

23 size_t thr_id_; //线程ID

24 bool bExit_; //线程是否要退出标志

25

26 apr_thread_t* thr_; //线程句柄

27 Condition cond_; //线程函数中等待任务的条件变量

28

29 private:

30 //not implement

31 Thread(const Thread& );

32 Thread& operator=(const Thread& );

33 };
一些说明:

我们在start()方法中调用apr库提供的线程API创建一个线程:
apr_thread_create(),并将线程函数thread_proc()和Thread*为线程函数参数传入apr_thread_create()即可,具体代码在后面贴出。
Join()函数用于等待线束线程,而destroy()
则是用于显示销毁该线程所占用的资源。
线程基类有一个纯虚函数run(),即应用线程继承Thread类后必须实现run()函数,即实现程序的业务逻辑
在start()创建完线程后系统便在某一时刻开始执行thread_proc()方法,我们在该方法中会调用run()函数,由于多态性,也就会调用应用程序多实现的run()函数了

具体实现(Thread.cpp):

1 int Thread::start()

2 {

3 apr_status_t rv;

4 apr_threadattr_t* thrattr = NULL;

5 apr_threadattr_create(&thrattr, g_mpool);

6

7 //创建一个线程

8 if ((rv = apr_thread_create(&thr_, thrattr, Thread::thread_proc, this, g_mpool)) != APR_SUCCESS)

9 {

10 set_error_code(rv);

11 char errbuf[512];

12 apr_strerror(rv, errbuf, sizeof(errbuf));

13 log2DebugView("Thread: failed create a thread: [%d][%s]\n", rv, errbuf);

14 return rv;

15 }

16 apr_sleep(100000); //ensure the thead_proc is running

17

18 return rv;

19 }

20 //等待线束线程

21 int Thread::join()

22 {

23 bExit_ = true;

24 notify();

25 apr_sleep(100000);

26

27 apr_status_t rv = 0;

28 return apr_thread_join(&rv, thr_);

29 }

30 //销毁线程

31 void Thread::destroy()

32 {

33 if (!bExit_)

34 join();

35 cond_.destroy();

36 }

37 //线程函数,将会调用子类实现的run()方法

38 void* Thread::thread_proc(apr_thread_t* th, void* data)

39 {

40 Thread* pthis = static_cast<Thread*>(data);

41 while (!pthis->bExit_)

42 {

43 //调用子类实现的run()方法

44 pthis->run();

45

46 //wait for signal

47 pthis->cond_.wait();

48 }

49

50 printf("thread exit, id: %d\n", pthis->get_thread_id());

51 apr_thread_exit(th, APR_SUCCESS);

52 return NULL;

53 }
这里我们不要太过意研究线程在具体代码是如何创建的,比如在start()函数中,在windows下线程函数可以是 UINT thread_proc(LPVOID param); 而创建线程则是调用__beginthreadex()的windows API即可,具体可参照windows的线程创建和销毁逻辑。线程使用如下:
应用示例

1 //继承Thread类并实现run()接口,有点类似Java或C#的用法

2 class MyThread : public Thread

3 {

4 public:

5 MyThread(){ loop_ = true; }

6 virtual MyThread(){}

7

8 //只关心如何实现业务逻辑,而看不到线程是如何创建的

9 virtual void run()

10 {

11 while (loop)

12 {

13 //do some work

14 }

15 printf("MyThread exit.\n");

16 }

17

18 private:

19 bool loop_;

20 };

21

22 // 在程序中使用如下

23 MyThread* pmt = new MyThread();

24 pmt->start(); //调用start()方法后,即启动了一个线程了

这样,我们就完成了一个线程类的封装和使用了,代码不多,但很常用哈。最后说明一下线程类中使用一个Condition的类,其实也就是一个对事件的封装使用,完全可以用windows下的 SetEvent()/WaitForSingleObject()替代或Linux下的pthread_condition_t的pthrad_condition_signal()/pthread_condition_wait()替代,即等待事件和通知事件的处理。

下一节我将会利用这个线程类实现一个简单的线程池,便于我们在程序中使用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: