使用C++11的function/bind組件封裝Thread以及回調函數的使用

以前在http://www.cnblogs.com/inevermore/p/4008572.html中採用面向對象的方式,封裝了Posix的線程,那裏採用的是虛函數+繼承的方式,用戶經過重寫Thread基類的run方法,傳入本身的用戶邏輯。html

 

如今咱們採用C++11的function,將函數做爲Thread類的成員,用戶只須要將function對象傳入線程便可,因此Thread的聲明中,應該含有一個function成員變量。編程

類的聲明以下:windows

#ifndef THREAD_H_
#define THREAD_H_

#include <boost/noncopyable.hpp>
#include <functional>
#include <pthread.h>

class Thread : boost::noncopyable
{
public:
    typedef std::function<void ()> ThreadCallback;

    Thread(ThreadCallback callback);
    ~Thread();

    void start();
    void join();

    static void *runInThread(void *);

private:
    pthread_t threadId_;
    bool isRunning_;
    ThreadCallback callback_; //回調函數
};



#endif //THREAD_H_

那麼如何開啓線程?思路與以前一致,寫一個static函數,用戶pthread_create的第三個參數,this做爲最後一個參數便可。函數

void Thread::start()
{
    pthread_create(&threadId_, NULL, runInThread, this);
    isRunning_ = true;
}

 

回調函數this

 

注意在這種封裝方式中,咱們採用了回調函數。回調函數與普通函數的區別就是,普通函數寫完由咱們本身直接調用,函數調用是一種不斷往上堆積的方式,而回調函數一般是咱們把某一個函數傳入一個「盒子」,由該盒子內的機制來調用它。spa

在這個例子裏面,咱們將function傳入Thread,當Thread啓動的時候,由Thread去執行function對象。線程

在win32編程中大量用到這種機制,咱們爲鼠標單擊、雙擊等事件編寫相應的函數,而後將其註冊給windows系統,而後系統在咱們觸發各類事件的時候,根據事件的類型,調用相應的構造函數。code

關於回調函數,能夠參考:http://www.zhihu.com/question/19801131htm

之後有時間,再專門總結下回調函數。對象

 

完整的cpp以下:

#include "Thread.h"

Thread::Thread(ThreadCallback callback)
: threadId_(0),
  isRunning_(false),
  callback_(std::move(callback))
{

}
    
Thread::~Thread()
{
    if(isRunning_)
    {
        //detach
        pthread_detach(threadId_);
    }
}

void Thread::start()
{
    pthread_create(&threadId_, NULL, runInThread, this);
    isRunning_ = true;
}
void Thread::join()
{
    pthread_join(threadId_, NULL);
    isRunning_ = false;
}

void *Thread::runInThread(void *arg)
{
    Thread *pt = static_cast<Thread*>(arg);
    pt->callback_(); //調用回調函數

    return NULL;
}

 

這個線程的使用方式有三種:

一是將普通函數做爲回調函數

void foo()
{
    while(1)
    {
        printf("foo\n");
        sleep(1);
    }
}

int main(int argc, char const *argv[])
{
    Thread t(&foo);

    t.start();
    t.join();

    return 0;
}

 

二是採用類的成員函數做爲回調函數:

class Foo
{
public:
    void foo(int i)
    {
        while(1)
        {
            printf("foo %d\n", i++);
            sleep(1);
        }
    }
};


int main(int argc, char const *argv[])
{
    Foo f;
    int i = 34;
    Thread t(bind(&Foo::foo, &f, i));

    t.start();
    t.join();

    return 0;
}

最後一種是組合一個新的線程類,注意這裏採用的是類的組合

class Foo
{
public:

    Foo()
    : thread_(bind(&Foo::foo, this))
    {
    }

    void start()
    {
        thread_.start();
        thread_.join();
    }

    void foo()
    {
        while(1)
        {
            printf("foo\n");
            sleep(1);
        }
    }
private:
    Thread thread_;
};

int main(int argc, char const *argv[])
{
    Foo f;
    f.start();

    return 0;
}

有些複雜的類,還須要將三種方式加以整合,例如後面要談到的TimerThread,裏面含有一個Thread和Timer,用戶將邏輯註冊給Timer,而後Timer的start函數註冊給Thread。

這種方式的Thread,使用靈活性相對於面向對象的風格,提升了不少。

 

基於對象和麪向對象

 

這裏總結幾點:

面向對象依靠的是虛函數+繼承,用戶經過重寫基類的虛函數,實現本身的邏輯。

基於對象,依賴類的組合,使用function和bind實現委託機制,更加依賴於回調函數傳入邏輯。

相關文章
相關標籤/搜索