從零開始構建一個Reactor模式的網路庫(二)執行緒類Thread
執行緒類Thread是對POSIX執行緒的封裝類,因為要構建的是一個Linux環境下的多執行緒網路庫,對執行緒的封裝是很必要的。
首先是CurrentThread名稱空間,主要是獲取以及快取執行緒id:
1 #ifndef CURRENTTHREAD_H 2 #define CURRENTTHREAD_H 3 #include <unistd.h> 4 #include <sys/syscall.h> 5 6 namespace CurrentThread 7 { 8 extern __thread int t_cachedTid; 9inline void cacheTid() 10 { 11 t_cachedTid=static_cast<int>(::syscall(SYS_gettid)); 12 } 13 inline int tid() 14 { 15 if(t_cachedTid==0) 16 cacheTid(); 17 return t_cachedTid; 18 } 19 } 20 21 #endif // CURRENTTHREAD_H
這裡要注意一個問題,就是執行緒id的獲取
執行緒id的獲取可以通過幾種方式,最方便的是syscall(),是一個glibc庫函式而不是一個系統呼叫,此時返回的是核心中的執行緒id。
因為Linux的執行緒實現中,執行緒事實上也是一個程序,因此返回的tid事實上就是pid,pid_t在Linux中一般實現為int。
另外,POSIX執行緒庫提供了函式pthread_self(),但是該函式返回的是程序中的執行緒id,型別為pthread_t,一般是unsigned long型別,並不是核心中的實際id,在除錯和排查錯誤時並不方便。
1 #ifndef THREAD_H 2 #define THREAD_H 3 #include <pthread.h> 4#include <memory> 5 #include <functional> 6 #include "Types.h" 7 #include <atomic> 8 #include "Mutex.h" 9 10 namespace mini 11 { 12 class Thread 13 { 14 public: 15 typedef std::function<void()> ThreadFunc; 16 explicit Thread(const ThreadFunc& func,const string& ThreadName=string()); 17 ~Thread(); 18 19 int join(); 20 void start(); 21 pid_t tid() const { return *tid_; } 22 bool started() const { return started_; } 23 const string& name() const {return name_;} 24 private: 25 26 void setDefaultName(); 27 28 bool started_; //if the thread started 29 bool joined_; //if joined settled 30 pthread_t pthreadId_; //thread id in the process 31 std::shared_ptr<pid_t> tid_; //shared_ptr to the tid 32 string name_; //name of the thread 33 ThreadFunc func_; //function for the thread 34 static std::atomic<int> numsCreated_; //record nums of created threads 35 }; 36 37 } 38 39 #endif // THREAD_H
Thread.h標頭檔案中定義了Thread類,成員包括啟動狀態、tid和pthreadid、實際執行的函式以及名字等。
1 #include "CurrentThread.h" 2 #include "Thread.h" 3 #include <stdio.h> 4 #include <unistd.h> 5 #include <sys/syscall.h> 6 #include <sys/types.h> 7 8 namespace CurrentThread 9 { 10 __thread int t_cachedTid=0; 11 /* 12 bool isMainThread() 13 { 14 return ::getpid()==tid(); 15 } 16 */ 17 } 18 namespace mini 19 { 20 struct ThreadData 21 { 22 typedef mini::Thread::ThreadFunc ThreadFunc; 23 ThreadFunc func_; 24 mini::string name_; 25 std::weak_ptr<pid_t> wkTid_; 26 ThreadData(const ThreadFunc& func,const string& name,const std::shared_ptr<pid_t>& tid) 27 :func_(func),name_(name),wkTid_(tid) 28 {} 29 void runInThread() 30 { 31 pid_t tid=CurrentThread::tid(); 32 std::shared_ptr<pid_t> ptid=wkTid_.lock(); 33 if(ptid) 34 { 35 *ptid=tid; 36 ptid.reset(); 37 } 38 func_(); 39 } 40 41 }; 42 std::atomic<int> Thread::numsCreated_(0); 43 44 void Thread::setDefaultName() 45 { 46 int num=numsCreated_++; 47 if(name_.empty()) 48 { 49 char buf[32]; 50 snprintf(buf,sizeof buf,"Thread%d", num); 51 name_=buf; 52 } 53 } 54 55 void* startThread(void* obj) 56 { 57 ThreadData* data=static_cast<ThreadData*>(obj); 58 data->runInThread(); 59 delete data; 60 return NULL; 61 } 62 63 Thread::Thread(const ThreadFunc &func, const string &ThreadName) 64 :started_(false), 65 joined_(false), 66 pthreadId_(0), 67 tid_(new pid_t(0)), 68 func_(func), 69 name_(ThreadName) 70 { 71 setDefaultName(); 72 } 73 74 void Thread::start() 75 { 76 started_=true; 77 ThreadData* data=new ThreadData(func_,name_,tid_); 78 if(pthread_create(&pthreadId_,NULL,&startThread,data)); 79 { 80 started_=false; 81 //delete data; 82 //LOG_SYSFATAL<<"FAILED in pthread_create"; 83 } 84 } 85 86 int Thread::join() 87 { 88 joined_=true; 89 return pthread_join(pthreadId_,NULL); 90 } 91 92 Thread::~Thread() 93 { 94 if(started_&&!joined_) 95 pthread_detach(pthreadId_); 96 } 97 }
由於向pthread_create中傳遞要執行的執行緒函式以及引數比較複雜,定義了一個內部類ThreadData。
ThreadData中有一個weak_ptr<pid_t>,當執行緒實際被建立並開始執行時,建立一個臨時的shared_ptr,確保Thread以及ThreadData被正確建立。
事實上,Thread類物件建立時,執行緒並沒有實際開始執行,直到呼叫start()才開始執行。
執行執行緒可以呼叫join()來等待Thread執行緒的結束。預設情況下,執行緒資源會保留直到呼叫pthread_join()。
解構函式簡單地呼叫pthread_detach()來設定執行緒分離,從而線上程函式執行結束後直接退出,執行緒資源也會線上程終止時立即被回收。