• linux下线程的两种封装方式


    在网络编程的时候往往需要对Linux下原生的pthread库中的函数进行封装,使其使用起来更加方便,封装方法一般有两种:面向对象和基于对象,下面将分别介绍这两种方式,最后统一分析这两种方式的优缺点:

    面向对象:

    面向对象的封装方式是通过虚函数提供回调功能,我们创建一个Thread类,然后设置一个run虚函数,使用时只需要重载这个类并实现其中的虚函数即可:

    具体实现如下:

    //------thread.h----------------
    
    #ifndef _THREAD_H_
    #define _THREAD_H_
    
    #include <pthread.h>
    
    class Thread
    {
    public:
        Thread();
        virtual ~Thread();
        void start();
        void join();
        void setAutoDelete(bool autoDelete);
    
    private:
        static void* threadRoutine(void* args);
        virtual void run() = 0;
        pthread_t threadId_;
        bool autoDelete_;
    };
    
    #endif
    详细实现如下:
    //---------thread.c-------------------
    
    #include "thread.h"
    #include <iostream>
    using namespace std;
    
    Thread::Thread() : autoDelete_(false)
    {
        cout << "Thread..." << endl;
    }
    
    Thread::~Thread()
    {
        cout << "~Thread..." << endl;
    }
    
    void* Thread::threadRoutine(void* arg)
    {
        Thread* thread = static_cast<Thread*>(arg);
        thread->run();
    
        if (thread->autoDelete_)
        {
            delete thread;
        }
    
        return NULL;
    }
    
    
    void Thread::start()
    {
        pthread_create(&threadId_,NULL, threadRoutine,this);
    }
    
    void Thread::join()
    {
        pthread_join(threadId_, NULL);
    }
    
    void Thread::setAutoDelete(bool autoDelete)
    {
        autoDelete_ = autoDelete;
    }
    View Code

    调用方式如下:

    #include "thread.h"
    #include <unistd.h>
    #include <iostream>
    using namespace std;
    
    class TestThread : public Thread
    {
    public:
        TestThread(int count) : count_(count)
        {
            cout << "TestThread..." << endl;
        }
        ~TestThread()
        {
            cout << "~TestThread..." << endl;
        }
    private:
        void run()
        {
            while( count_-- )
            {
                cout << "This is a test" << endl;
                sleep(1);
            }
        }
    
    private:
        int count_;
    };
    
    
    int main(void)
    {
        TestThread t(5);
        t.start();
        t.join();
    
        //do other work....
    }
    View Code

      可以看到,其中有一个私有的重虚函数run,使用时只需要继承thread,实现run函数,并在其内实现线程需要执行的逻辑就可以了。

      同时有一个静态的threadRoutine成员函数,因为C++成员函数缺省的调用方式是__thiscall,成员函数中隐含的第一个参数都是this指针,所以不能匹配给pthread_create的形参void *(*start_routine) (void *), 这时候就可以传递类的一个静态成员,把this指针做为该静态成员的参数。也就是start方法的: pthread_create(&threadId_,NULL, threadRoutine,this);   在threadRoutine函数中将接收到的void*类型的参数转换成Thread*就可以了,然后调用他的run方法。

      在main函数中静态创建了一个TestThread变量 t,然后启动线程, 但是如果线程运行结束了,这个变量 t 就失去存在的价值了, 总不能一直让它在哪里占用着栈上空间吧,此时我们就可以动态创建TestThread, 然后设定其autoDelete_属性,这样当该线程执行完毕后就会检查是否需要delete掉这个堆上变量,这就是autoDelete_属性的作用,可以将main函数修改如下:

    int main(void)
    {
        TestThread *t = new TestThread(5);
        t->setAutoDelete(true);
        t->start();
        t->join();
    
           //do other work....
    
    }
    View Code

      如果你决心只在堆上分配的话,就可以将析构函数设置为私有的,这样就可以只在堆上分配,但是你还得提供一个公有的自定义的析构方法。

    基于对象

      基于对象的封装方法,实现思路同上,但是它不是通过虚函数的方式实现回调的功能,而是通过函数绑定的方式, boost库中的bind/function可以实现将一个函数转换成另一种函数,就算是成员函数也可以,它相当于C++中的bind1st,bin2nd等函数适配器,在C++11中也实现了bind/function, 关于boost bind/function的使用方法,可以看这里。这里不做过多介绍。

    用基于对象的封装方法如下:

    //-------thread.h-------
    
    #ifndef _THREAD_H_
    #define _THREAD_H_
    
    #include <pthread.h>
    #include <boost/function.hpp>
    
    class Thread
    {
    public:
        typedef boost::function<void ()> ThreadFunc;
        explicit Thread(const ThreadFunc& func);
        ~Thread();
        void start();
        void join();
        void setAutoDelete(bool autoDelete);
    
    private:
        static void* threadRoutine(void* args);
        void run();
        bool autoDelete_;
        ThreadFunc func_;
        pthread_t threadId_;
    
    };
    
    #endif

    其中在构造函数中需要ThreadFunc对象,这个函数就是在run中调用的实例,可以看下面代码:

    //---------thread.cpp-----------
    
    #include "thread.h"
    #include <iostream>
    using namespace std;
    
    
    Thread::Thread(const ThreadFunc& func):autoDelete_(false),func_(func)
    {
        cout << "Thread..." << endl;
    }
    
    
    Thread::~Thread()
    {
        cout << "~Thread..." << endl;
    }
    
    
    void Thread::run()
    {
        func_();
    }
    
    void* Thread::threadRoutine(void* arg)
    {
        Thread* thread = static_cast<Thread*>(arg);
        thread->run();
    
        if (thread->autoDelete_)
        {
            delete thread;
        }
    
        return NULL;
    }
    
    
    void Thread::start()
    {
        pthread_create(&threadId_,NULL, threadRoutine,this);
    }
    
    
    void Thread::join()
    {
        pthread_join(threadId_, NULL);
    }
    
    
    void Thread::setAutoDelete(bool autoDelete)
    {
        autoDelete_ = autoDelete;
    }
    View Code

    在主函数中调用方法也很简单:

    #include "thread.h"
    #include <unistd.h>
    #include <iostream>
    #include <boost/bind.hpp>
    using namespace std;
    
    void ThreadFunc()
    {
        cout << "ThreadFunc..." << endl;
    }
    
    void ThreadFunc2(int count)
    {
        while( count_-- )
        {
            cout << "This is a test" << endl;
            sleep(1);
        }
    }
    
    class Foo
    {
    public:
        Foo(int count) : count_(count){    }
    
        void MemerFunc()
        {
            while( count_-- )
            {
                cout << "This is a test" << endl;
                sleep(1);
            }
        }
    private:
        int count_;
    
    };
    
    int main(void)
    {
    
    
        //Thread *t = new Thread(ThreadFunc);    
      
        //Thread *t = new Thread(boost::bind(ThreadFunc2,4));
        
        Foo foo(3);
        Thread *t = new Thread(boost::bind(&Foo::MemerFunc,&foo));
    
        t->setAutoDelete(true);
        t->start();
        t->join();
    
        //do other work....
    
    }
    View Code

    在上面的例子中我们可以绑定不同的对象到Thread的构造函数中,这样就可以实现随着绑定的不同调用不同的函数。

    上面就是线程用面向对象和基于对象的不同封装方式,如果你要问,它们孰优孰劣,请挪步这篇精彩的分析

     

  • 相关阅读:
    Mac 删除并关闭.DS_Store
    python 使用AES加密16位
    python 使用AES加密时,提示模块不存在
    sql大全
    Mac 升级10.15后,安装软件时提示‘文件已损坏’的解决办法
    Django model Datefield和DateTimeField类型
    1013. 将数组分成和相等的三个部分
    python 批量pip安装包
    Ajax发送数据
    kafka实战教程(python操作kafka--理论篇)
  • 原文地址:https://www.cnblogs.com/gaorong/p/6479493.html
Copyright © 2020-2023  润新知