线程
(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; }