代码文件目录为:muduo/base
ThreadNameInitializer进行主线程初始化操作(利用全局变量):包括设置默认的线程name、缓存线程id。如果进行了fork,那么在子进程中运行afterFork
函数进行同样的初始化工作。
void afterFork()
{
muduo::CurrentThread::t_cachedTid = 0;
muduo::CurrentThread::t_threadName = "main";
CurrentThread::tid();
// no need to call pthread_atfork(NULL, NULL, &afterFork);
}
class ThreadNameInitializer
{
public:
ThreadNameInitializer()
{
muduo::CurrentThread::t_threadName = "main";
CurrentThread::tid();
pthread_atfork(NULL, NULL, &afterFork);
}
};
ThreadNameInitializer init;
Thread.h
class Thread : boost::noncopyable //不允许复制
{
public:
typedef boost::function ThreadFunc;
explicit Thread(const ThreadFunc&, const string& name = string());
#ifdef __GXX_EXPERIMENTAL_CXX0X__
explicit Thread(ThreadFunc&&, const string& name = string());
#endif
~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_; } //返回线程id
const string& name() const { return name_; } //返回线程名
static int numCreated() { return numCreated_.get(); } //返回创建的线程数
private:
void setDefaultName();
bool started_; //启动标识
bool joined_;
pthread_t pthreadId_; // pthread_t给pthraed_xxx函数使用
pid_t tid_; // pid_t作为线程标识
ThreadFunc func_;
string name_;
CountDownLatch latch_;
static AtomicInt32 numCreated_; //记录创建了多少线程
};
Thread既有pthread_t
也有pid_t
,它们各有用处,pthread_t
给pthread_XXX函数使用,而pid_t
作为线程标识。
为什么使用pid_t而不使用pthread_t来标识线程id呢?
pthread_t的值很大,无法作为一些容器的key值。 glibc的Pthreads实现实际上把pthread_t作为一个结构体指针,指向一块动态分配的内存,但是这块内存是可以反复使用的,也就是说很容易造成pthread_t的重复。也就是说pthreads只能保证同一进程内,同一时刻的各个线程不同;不能保证同一个进程全程时段每个线程具有不同的id,不能保证线程id的唯一性。
在LINUX系统中,建议使用gettid()系统调用的返回值作为线程id,这么做的原因:
返回值是一个pid_t,其值是一个很小的整数,方便输出。
在linux系统中,它直接标识内核任务调度id,可通过/proc文件系统中找到对应项:/proc/tid 或者 /proc/pid/task/tid,方便定位到具体线程。任何时刻都是唯一的,并且由于linux分配新的pid采用递增轮回办法,短时间内启动多个线程也会具有不同的id。
Thread::Thread(const ThreadFunc& func, const string& n)
: started_(false),
joined_(false),
pthreadId_(0),
tid_(0),
func_(func),
name_(n),
latch_(1)
{
setDefaultName();
}
线程池中调用Thread构造函数如下:
//将this分配给runInThread,相当于构造Thread(this.runInThread,name+id)并加入线程数组。线程函数是runInThread
threads_.push_back(new muduo::Thread(
boost::bind(&ThreadPool::runInThread, this), name_+id));
threads_[i].start(); //启动每个线程,但是由于线程运行的函数是runInThread,所以会阻塞
线程函数即为:this.runInThread,线程名为name+id。 (注意此处的runInThread不是Thread类中的,而是ThreadPool中的)
线程的默认name与它是第几个线程相关,std::string 没有format,那么格式化可以使用snprintf,或者使用ostringstream,或者boost::format也是可以的。
void Thread::setDefaultName()
{
int num = numCreated_.incrementAndGet();
if (name_.empty())
{
char buf[32];
snprintf(buf, sizeof buf, "Thread%d", num);
name_ = buf;
//name_ = str(boost::format("Thread%1%") % num); // 使用boost::format
}
}
线程启动函数,调用pthread_create创建线程,线程函数为detail::startThread,传递给线程函数的参数data是在heap上分配的,data存放了线程真正要执行的函数记为func、线程id、线程name等信息。detail::startThread会调用func启动线程,所以detail::startThread可以看成是一个跳板或中介。
void Thread::start() //线程启动函数,调用pthread_create创建线程
{
assert(!started_); //确保线程没有启动
started_ = true; //设置标记,线程已经启动
// FIXME: move(func_)
detail::ThreadData* data = new detail::ThreadData(func_, name_, &tid_, &latch_); //data存放了线程真正要执行的函数,记为func,线程id,线程name等信息
//创建线程:线程函数为detail::startThread
if (pthread_create(&pthreadId_, NULL, &detail::startThread, data))
{
started_ = false; //创建线程失败,设置标记线程未启动
delete data; // or no delete?
LOG_SYSFATAL << "Failed in pthread_create";
}
else
{
latch_.wait();
assert(tid_ > 0);
}
}
detail::startThread首先将参数转型为ThreadData*,然后调用data->runInThread()。
void* startThread(void* obj)
{
ThreadData* data = static_cast(obj);
data->runInThread();
delete data;
return NULL;
}
runInThread()最终会调用func()。 (真正的线程函数)
void runInThread()
{
*tid_ = muduo::CurrentThread::tid();
tid_ = NULL;
latch_->countDown();
latch_ = NULL;
muduo::CurrentThread::t_threadName = name_.empty() ? "muduoThread" : name_.c_str();
::prctl(PR_SET_NAME, muduo::CurrentThread::t_threadName); //设置名字
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
}
}
struct ThreadData
{
typedef muduo::Thread::ThreadFunc ThreadFunc;
ThreadFunc func_;
string name_;
pid_t* tid_;
CountDownLatch* latch_;
ThreadData(const ThreadFunc& func,
const string& name,
pid_t* tid,
CountDownLatch* latch)
: func_(func),
name_(name),
tid_(tid),
latch_(latch)
{ }
//....
注意:
为什么不能直接在创建线程的时候执行某个类的成员函数?
因为pthread_create
的线程函数定义为void *func(void*)
,无法将non-staic成员函数传递给pthread_create
。
试想,如果pthread_create
的线程函数参数定义为boost::function
,那么结合boost::bind,就可以将一个成员函数作为参数了,像这样:
pthread_create(&tid, NULL, boost::bind(&Class::func, &obj, _1), arg);
所以boost::function和boost::bind还是挺强大的。在C++11中已经成为标准纳入到std中了。