zoukankan      html  css  js  c++  java
  • 【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()替代,即等待事件和通知事件的处理。
        下一节我将会利用这个线程类实现一个简单的线程池,便于我们在程序中使用。

  • 相关阅读:
    Algebra, Topology, Differential Calculus, and Optimization Theory For Computer Science and Machine Learning 第4章 读书笔记(待更新)
    Algebra, Topology, Differential Calculus, and Optimization Theory For Computer Science and Machine Learning 第3章 读书笔记(待更新)
    Algebra, Topology, Differential Calculus, and Optimization Theory For Computer Science and Machine Learning 第1,2章 读书笔记(待更新)
    Tkinter的Message组件
    Git 实操/配置/实践
    mysq5.7.32-win安装步骤
    行为型模式之模板方法
    结构型模式之组合模式
    结构型模式之享元模式
    结构型模式之外观模式
  • 原文地址:https://www.cnblogs.com/huty/p/8517390.html
Copyright © 2011-2022 走看看