• 【C/C++开发】C++实现简单的线程类


    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] ", 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 ", 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. ");
    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()替代,即等待事件和通知事件的处理。
        下一节我将会利用这个线程类实现一个简单的线程池,便于我们在程序中使用。

  • 相关阅读:
    Spring Boot & ES 实战,值得参考!
    什么是 Java 对象深拷贝?面试必问!
    一份完整的 MySQL 开发规范,进大厂必看!
    ASP.NET + MVC5 入门完整教程五 --- Razor (模型与布局)
    C# MVC扩展方法
    C#泛型应用及原理
    部分类及部分方法
    程序集
    类的可访问属性
    ASP.NET + MVC5 入门完整教程四---MVC 中使用扩展方法
  • 原文地址:https://www.cnblogs.com/huty/p/8517390.html
Copyright © 2020-2023  润新知