c++封裝線程


線程

(1)Linux線程庫有兩個流行的線程庫,分別是LinuxThreads和NPTL,由於LinuxThreads的某些缺點,已經被NPTL取代,它們都是基於1:1模式實現,即1個用戶線程被映射爲1個內核線程;故每一個用戶線程在內核中有唯一的標識

線程標識

使用pthread_t有問題!!!

void* loop(void*)  
{  
  cout  << "Thread run" << endl;  
  return NULL;  
}  
  
int main()  
{  
  pthread_t t1, t2;  
  
  errno = pthread_create(&t1, NULL, loop, NULL);  
  assert(!errno);  
  pthread_join(t1, NULL);  
  
  pthread_create(&t2, NULL, loop, NULL);  
  assert(!errno);  
  pthread_join(t2, NULL);  
  
  cout  << hex << t1 << " " << t2 << endl;  
  
  return 0;  
}  

說明幾點:

(1)驗證該代碼片段可得:上述t1和t2值相同;Pthreads只能保證同一進程中各線程pthread_t在同一時刻不同,不能保證同一線程具有不同的id;

(2)pthread_t在某些應用場合不能作爲線程的標識,比如一個線程已經死亡,而此時創建一個新的線程,可能重用了舊的pthread_t,導致本來與舊線程交互的數據,開始又和新的線程錯誤執行;

(3)解決辦法:由於NPTL基於1:1模式(1個用戶線程被映射爲1個內核線程)實現;故每一個用戶線程在內核中也有唯一的標識,使用gettid()可以獲得該標識,爲了減少系統調用次數,可使用__thread進行緩存該id,這樣只有第一次使用該id纔會調用gettid()系統調用,但是可移植性會降低;下述代碼中__thread緩存tid;syscall(SYS_gettid)爲直接使用系統調用號執行系統調用,即直接獲取某執行線程的唯一線程ID;

實現代碼如下:

namespace CurrentThread  
{  
  
pid_t gettid()  
{  
  return syscall(SYS_gettid);  
}  
  
__thread pid_t t_tid;  
  
static pthread_t tid()  
{  
  if (t_tid == 0)  
    {  
      t_tid = gettid();  
    }  
  
  return t_tid;  
}  
  
}  

  1. thread聲明如下:
  1. namespace CurrentThread  
    {  
      
    extern __thread pid_t t_tid;  
    extern pid_t tid();  
      
    }  
      
    //notice: we should make Thread object's life longer than thread, so we shold always use fun: join,  
    class Thread final  
    {  
    public:  
      Thread(const Thread&) = delete;  
      Thread& operator=(const Thread&) = delete;  
      
      typedef std::function<void()> ThreadFunc;  
      
      explicit Thread(const ThreadFunc& func);  
      
      void start();  
      
      bool running() const  
      {  
        return _running;  
      }  
      
      int join()  
      {  
        int err = pthread_join(_threadId, NULL);  
        return err;  
      }  
      
      pid_t tid() const  
      {  
        return _tid;  
      }  
      
    private:  
      void _runInThread();  
      static void* _threadFunc(void*);  
      
      pid_t _tid;  
      pthread_t _threadId;  
      ThreadFunc _func;  
      bool _running;  
    };  

說明幾點:

(1)_tid爲線程的唯一標識,也就是利用上述CurrentThread和__thread來實現獲得的;

(2)線程與Thread對象的生命週期,要注意Thread對象析構和線程終止的順序關係;當調用thread.join()函數時,可以保證Thread對象在線程終止之後析構;若沒有調用thread.join()函數,將會出現thread先析構的情況,此時C++析構會自動deatch線程,因此保證資源不會泄漏,但是線程在使用繼續使用Thread相關資源時,執行過程將會出現未定義情況;因此我們應該保證使用thread.join()函數,即Thread的生命週期要長於線程的生命週期;

thread實現如下:

  1. __thread pid_t  CurrentThread::t_tid = 0;  
      
    pid_t CurrentThread::tid()  
    {  
      if (t_tid == 0)  
        t_tid = syscall(SYS_gettid);  
      
      return t_tid;  
    }  
      
    Thread::Thread(const ThreadFunc& func):  
        _func(func),  
        _running(false)  
    {  
      
    }  
      
    void Thread::start()  
    {  
      assert(!_running);  
      _running = true;  
      
      int err = pthread_create(&_threadId, NULL, Thread::_threadFunc, this);  
      if (err != 0)  
        {  
          cout << "pthread_create system error";  
        }  
    }  
      
    void Thread::_runInThread()  
    {  
      assert(_running);  
      try  
        {  
          _tid = CurrentThread::tid();       //impotant, the time we obtain tid;  
      
          cout << "Thread tid[" << _tid << "] is threadFunc";  
      
          if (_func)  
            _func();  
        }  
      catch (...)  
        {  
          cout << "Exception happen in Thread";  
        }  
    }  
      
    void* Thread::_threadFunc(void* obj)      //remember obj's scope in self class  
    {  
      Thread* thread = static_cast<Thread*>(obj);  
      thread->_runInThread();  
      return NULL;  
    }  

說明幾點:

(1)static void* _threadFunc(void*)爲Thread的static成員函數,而不是成員函數,因爲pthread_create(&_threadId, NULL, Thread::_threadFunc, this)接收的函數類型必須是一個類似全局的函數,使用非static成員函數,顯然我們還要傳遞相對應的對象指針,pthread_create是無法接收的;
(2)在_runInThread()中,使用 catch (...)捕捉線程執行過程中的一切異常;
測試代碼:
  1. #include <Base/Thread.h>  
    #include <stdio.h>  
      
    using namespace std;  
    using namespace Base;  
      
    void func1() {  
      printf("%s %d\n", "func1: tid", CurrentThread::tid());  
    }  
      
    void func2() {  
       printf("%s %d\n", "func2: tid", CurrentThread::tid());  
    }  
      
    int main(void) {  
      Thread thread1(func1);  
      Thread thread2(func2);  
        
      thread1.start();  
      thread2.start();  
        
      thread1.join();  
      thread2.join();  
      return 0;  
    }  





發佈了25 篇原創文章 · 獲贊 1 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章