muduo源码分析:线程类Thread封装

时间:2021-02-06 00:03:38

线程id

Linux中,每个进程有一个pid,类型pid_t,由getpid()取得。

Linux下的POSIX线程也有一个id,类型 pthread_t,由pthread_self()取得,该id由线程库维护,其id空间是各个进程独立的(即不同进程中的线程可能有相同的id)。Linux中的POSIX线程库实现的线程其实也是一个进程(LWP),只是该进程与主进程(启动线程的进程)共享一些资源而已,比如代码段,数据段等。

有时候我们可能需要知道线程的真实pid。比如进程P1要向另外一个进程P2中的某个线程发送信号时,既不能使用P2的pid,更不能使用线程的pthread id,而只能使用该线程的真实pid,称为tid
有一个函数gettid()可以得到tid,但glibc并没有实现该函数,只能通过Linux的系统调用syscall来获取。

return syscall(SYS_gettid)

线程私有数据

1.__thread : gcc内置的线程局部存储设施

__thread只能修饰POD类型
POD类型(plain old data),与C兼容的原始数据,例如,结构和整型等C语言中的类型是 POD 类型,但带有用户定义的构造函数或虚函数的类则不是
__thread string t_obj1(“cppcourse”); // 错误,不能调用对象的构造函数
__thread string* t_obj2 = new string; // 错误,初始化只能是编译期常量
__thread string* t_obj3 = NULL; // 正确

2.Posix线程私有数据

(Thread-specific Date)TSD
相当于二级索引,key数组(一级索引)整个进程共享,标志哪些key使用与否,每个线程有自己pkey数组(二级索引),存在pthread结构中,pkey数组存储自己私有数据的指针。key --》pkey- -》私有数据指针

pthread_key_create(创建一个键),pthread_setspecific(为一个键设置线程私有数据),pthread_getspecific(从一个键读取线程私有数据),pthread_key_delete(删除一个键)。这几个函数的声明如下:
#include <pthread.h>
int pthread_key_create(pthread_key_t *key,void (*destr_function)(void *));
int pthread_setspecific(pthread_key_t key,const void *pointer));
void *pthread_getspecific(pthread_key_t key);
int pthread_key_delete(pthread_key_t key);

boost :: is_same

类型是否一样

存储类:

存储类可分为auto、register、static、extern、mutable、thread_local(__thread)等

mutable

mutable存储类只能用于类的数据成员,不能用于普通变量。具有mutable性质的类的数据成员打破了类对象的const限定,允许修改类的mutable的数据成员,即便类的其它成员仍然是const只读属性。

thread_local

适用于命名空间内的变量、全局作用域的变量、函数内部定义的静态变量,如果使用了thread_local关键字,则在 运行时不同的线程具有该变量的不同的存储位置,即各有各的副本。因此,具有thread_local存储类的变量,必然具有static存储类性质,不管是否使用了static关键字
class CurrenThread{
    public: static __thread  int a;
}; 
  __thread int test::a=0;

源码分析

detail命名空间:一些公共、初始化操作,其中有调用CurrentThread::tid( )
CurrentThread::tid() 在CurrentThread 命名空间,也有调用detail的detail::gettid( )
他们有相互调用
CurrentThread命名空间:本线程基本信息,没有封装成类(类用__thread麻烦),直接裸露在命名空间里,都是__thread 私有数据

ThreadNameInitalizer init;//全局变量,在main函数开始前就构造,完成启动初始化配置(pthread_atfork),且就一次,这一次就标示了 主线程的名字CurrenThread::t_ThreadName="main"  

//Thread.h
#ifndef MUDUO_BASE_THREAD_H
#define MUDUO_BASE_THREAD_H

#include <muduo/base/Atomic.h>
#include <muduo/base/Types.h>

#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <pthread.h>

namespace muduo
{

class Thread : boost::noncopyable
{
public:
	typedef boost::function<void ()> ThreadFunc;
	explicit Thread(const ThreadFunc&,const string& name=string());
	~Thread();

	void start();
	int join();// return pthread_join

	bool started() const {return started_;}
	pthread_t pthreadId() const {return pthreadId_;}
	pid_t tid() const {return tid_;}

	static int numCreated() {return numCreated_.get();}

private:
	static void*	startThread(void* thread);
	void			runInThread();
	
	bool		started_;
	pthread_t	pthreadId_;
	pid_t		tid_;
	ThreadFunc	func_;
	string		name_;

	static AtomicInt32 numCreated_;
};

}

#endif
//CurrentThread.h
namespace muduo
{
namespace CurrentThread
{
	//internal
	extern	__thread int t_cachedTid;
	extern __thread char t_tidString[32];
	extern __thread const char* t_threadName;
	void cacheTid();

	inline int tid()
	{
		if(t_cachedTid==0)
		  cacheTid();
		return t_cachedTid;
	}

	inline const char* tidString()
	{
		return t_tidString;
	}

	inline const char* name()
	{
		return t_threadName;
	}

	bool isMainThread();

}
}

#endif
//Thread.cc
#include <muduo/base/Thread.h>
#include <muduo/base/CurrentThread.h>
#include <muduo/base/Exception.h>
//#include <muduo/base/Logging.h>

#include <boost/static_assert.hpp>
#include <boost/type_traits/is_same.hpp>

#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <linux/unistd.h>

namespace muduo
{

/*
class CurrenThread{
	public: static __thread  int a;
}; 
__thread int test::a=0;

*/
//CurrentThread 线程信息,如果组织成类就得像上面那样麻烦,所以直接裸露在命名空间里,全是私有全局变量
namespace CurrentThread
{
	__thread int t_cachedTid = 0;//真实pid
	__thread char t_tidString[32];
	__thread const char* t_threadName = "unknown";//其实只有main,started,finished,crashed这四个名字( 在Thread::runInThread()中对其修改 ),用来表示线程状态的而已,线程真正名字时Thread::name_
	const bool sameType = boost::is_same<int, pid_t>::value;
	BOOST_STATIC_ASSERT(sameType);
}

//一些公共初始化操作
namespace detail
{

pid_t gettid()
{
	return static_cast<pid_t>(::syscall(SYS_gettid));
}

//设置fork后的主线程状态,不需要显示调用,下面用全局变量ThreadNameInitalizer的构造函数调用了
void afterFork()
{
	muduo::CurrentThread::t_cachedTid=0;
	muduo::CurrentThread::t_threadName="main";
	CurrentThread::tid();//如果t_cachedTid等于0,就先调用cachedTid(),在返回t_cachedTide.其实这句就是为了缓存t_cachedTid( detail 中调用 CurrentThread)
}

class ThreadNameInitalizer
{
public:
	ThreadNameInitalizer()
	{
		muduo::CurrentThread::t_threadName = "main";
		CurrentThread::tid();
		pthread_atfork(NULL,NULL,&afterFork);
	}

};

ThreadNameInitalizer init;//全局变量,在main函数开始前就初始化,且就一次,这一次就标示了main 主线程

}

using namespace muduo;
void CurrentThread::cacheTid()
{
	if(t_cachedTid == 0)
	{
		t_cachedTid = detail::gettid();//CurrentThread 调用 detail
		int n = snprintf(t_tidString, sizeof t_tidString, "%5d ", t_cachedTid); 
		 assert(n == 6); (void) n;   
	}
}

bool CurrentThread::isMainThread()
{
	return tid() == ::getpid();
}

AtomicInt32 Thread
::numCreated_;//因为numCreated_是静态的,一定要定义,不能只在头文件中声明
Thread::Thread(const ThreadFunc& func, const string& n)
		:started_(false),
		 pthreadId_(0),
		 tid_(0),
		 func_(func),
		 name_(n)
{
	numCreated_.increment();
}

Thread::~Thread()
{}

void Thread::start()
{
	assert(!started_);
	started_=true;
	errno = pthread_create(&pthreadId_, NULL, &startThread, this);
	if(errno != 0)
	{
		//LOG_SYSFATAL << "Failed in pthread_create";
	}
}

int Thread::join()
{
	assert(started_);
	return pthread_join(pthreadId_,NULL);
}

void* Thread::startThread(void* obj)
{
	Thread* thread=static_cast<Thread*>(obj);
	thread->runInThread();
	return NULL;
}

void Thread::runInThread()
{
	tid_=CurrentThread::tid();
	muduo::CurrentThread::t_threadName=name_.c_str();
	try
	{
		func_();
		muduo::CurrentThread::t_threadName="finished";
	}
	catch(const Exception& ex)
	{
		muduo::CurrentThread::t_threadName="crashed";
		fprintf(stderr, "exception caught in Thread %s\n", name_.c_str());
		fprintf(stderr, "reason: %s\n", ex.what());
		fprintf(stderr, "stack trace: %s\n", ex.stackTrace());
		abort();
	  }
	  catch (const std::exception& ex)
	  {
		muduo::CurrentThread::t_threadName = "crashed";
		fprintf(stderr, "exception caught in Thread %s\n", name_.c_str());
		fprintf(stderr, "reason: %s\n", ex.what());
		abort();
	  }
	  catch (...)
	  {
		muduo::CurrentThread::t_threadName = "crashed";
		fprintf(stderr, "unknown exception caught in Thread %s\n", name_.c_str());
		throw; // rethrow
	  }
}
	
}
相关: 线程属性与线程局部存储

参考:c++教程网

           muduo网络库

           linux多线程服务器端编程》.陈硕