線程
(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;
}
}
- thread聲明如下:
-
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實現如下:
-
__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; }
說明幾點:
-
#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; }