以前在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實現委託機制,更加依賴於回調函數傳入邏輯。