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

多线程编程简介:

大家在编程时,经常需要在程序中启动一个或多个线程来处理任务,而如果每次都是去调用系统创建线程的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 };

一些说明:

  1. 我们在start()方法中调用apr库提供的线程API创建一个线程: apr_thread_create(),并将线程函数thread_proc()和Thread*为线程函数参数传入apr_thread_create()即可,具体代码在后面贴出。
  2. Join()函数用于等待线束线程,而destroy() 则是用于显示销毁该线程所占用的资源。
  3. 线程基类有一个纯虚函数run(),即应用线程继承Thread类后必须实现run()函数,即实现程序的业务逻辑
  4. 在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()替代,即等待事件和通知事件的处理。

    下一节我将会利用这个线程类实现一个简单的线程池,便于我们在程序中使用。

【C/C++开发】C++实现简单的线程类的更多相关文章

  1. Delphi线程类 DIY(把类指针作为参数传进去,就可以执行类里面的方法啦)

    Delphi 封装了一个很强大的线程类 TThread, 我们也自己动手制作一个简单的线程类 首先Type一个类 type TwwThread = class constructor Create;  ...

  2. 【C/C++开发】C++实现简单的线程池

    C++实现简单的线程池 线程池编程简介: 在我们的服务端的程序中运用了大量关于池的概念,线程池.连接池.内存池.对象池等等.使用池的概念后可以高效利用服务器端的资源,比如没有大量的线程在系统中进行上下 ...

  3. iOS开发网络篇—简单介绍ASI框架的使用

    iOS开发网络篇—简单介绍ASI框架的使用 说明:本文主要介绍网络编程中常用框架ASI的简单使用. 一.ASI简单介绍 ASI:全称是ASIHTTPRequest,外号“HTTP终结者”,功能十分强大 ...

  4. 封装一个简单好用的打印Log的工具类And快速开发系列 10个常用工具类

    快速开发系列 10个常用工具类 http://blog.csdn.net/lmj623565791/article/details/38965311 ------------------------- ...

  5. java开发两年,这些线程知识你都不知道,你怎么涨薪?

    前言 什么是线程:程序中负责执行的哪个东东就叫做线程(执行路线,进程内部的执行序列),或者说是进程的子任务. Java中实现多线程有几种方法 继承Thread类: 实现Runnable接口: 实现Ca ...

  6. iOS开发UI篇—简单的浏览器查看程序

    iOS开发UI篇—简单的浏览器查看程序 一.程序实现要求 1.要求 2. 界面分析 (1) 需要读取或修改属性的控件需要设置属性 序号标签 图片 图片描述 左边按钮 右边按钮 (2) 需要监听响应事件 ...

  7. iOS开发UI篇—简单介绍静态单元格的使用

    iOS开发UI篇—简单介绍静态单元格的使用 一.实现效果与说明 说明:观察上面的展示效果,可以发现整个界面是由一个tableview来展示的,上面的数据都是固定的,且几乎不会改变. 要完成上面的效果, ...

  8. iOS开发Swift篇—简单介绍

    iOS开发Swift篇—简单介绍 一.简介 Swift是苹果于2014年WWDC(苹果开发者大会)发布的全新编程语言 Swift在天朝译为“雨燕”,是它的LOGO 是一只燕子,跟Objective-C ...

  9. 李洪强iOS开发之- 实现简单的弹窗

     李洪强iOS开发之- 实现简单的弹窗 实现的效果:  112222222222223333333333333333

随机推荐

  1. Greenplum 调优--数据倾斜排查(二)

    上次有个朋友咨询我一个GP数据倾斜的问题,他说查看gp_toolkit.gp_skew_coefficients表时花费了20-30分钟左右才出来结果,后来指导他分析原因并给出其他方案来查看数据倾斜. ...

  2. 三十三.mysqldump 实时增量备份 、innobackupex

    1.数据库备份与恢复 逻辑备份工具 mysqldump 使用mysql 恢复数据库   1.1备份MySQL服务器上的所有库 ]# mysqldump -u root -p123456 --all-d ...

  3. 后缀数组 TYVJ P1860 后缀数组

    /*P1860 后缀数组时间: 1000ms / 空间: 131072KiB / Java类名: Main描述 我们定义一个字符串的后缀suffix(i)表示从s[i]到s[length(s)]这段子 ...

  4. mui Picker DtPicker 固定内容以及变动内容Picker 不同情况下的应用 提高效率

    目的是: 保证不同的Picker只创建一次,多次创建Picker系统不会自己销毁,X掉一个之后后面可能会出现下一个. 保证实现需求的情况下,优化代码,提高效率. 1.如果picker中的内容为定值 v ...

  5. Java学习日记基础篇(四)——类,对象之成员变量,成员方法,构造方法

    面向对象(Object Oriented) 一.面向对象杂谈 面向对象(Object Oriented),我的翻译是以物体为目标的,就是说编程的时候是建立一个物体,然后对这个物体进行操作. Java语 ...

  6. MySQL的sql解析

    首先看一下示例语句 SELECT DISTINCT  < select_list > FROM  < left_table > < join_type > JOIN ...

  7. vue中的父组件传值给子组件

    以上父组件以及父组件里面的代码 下面是子组件以及里面的代码

  8. meshing-划分圆柱结构化网格

    原视频下载地址:https://yunpan.cn/cqjeckrzEpVkY  访问密码 eb5d

  9. CodeForces - 1189E Count Pairs(平方差)

    Count Pairs You are given a prime number pp, nn integers a1,a2,…,ana1,a2,…,an, and an integer kk. Fi ...

  10. arcgis python 获得arcgis安装版本和安装位置

    import arcpy print(arcpy.GetInstallInfo()['Version']) 和获得ArcGIS版本和安装位置 import arcpy # Use the dictio ...