关于EventLoopThread有以下几点:
1.任何一个线程,只要创建并运行了EventLoop,都称之为I/O线程。
2.I/O线程不一定是主线程。I/O线程中可能有I/O线程池和计算线程池。
3.muduo并发模型one loop per thread + threadpool
4.为了方便使用,就直接定义了一个I/O线程的类,就是EventLoopThread类,该类实际上就是对I/O线程的封装。
(1)EventLoopThread创建了一个线程。
(2)在该类线程函数中创建了一个EventLoop对象并调用EventLoop::loop
我们来看一下它的分析,后面会有示例:
头文件:
#include
#include
#include
#include
namespace muduo
{
namespace net
{
class EventLoop;
class EventLoopThread : boost::noncopyable
{
public:
typedef boost::function ThreadInitCallback;
EventLoopThread(const ThreadInitCallback& cb = ThreadInitCallback(),
const string& name = string());
~EventLoopThread();
EventLoop* startLoop(); //启动成员thread_线程,该线程就成了I/O线程,内部调用thread_.start()
private:
void threadFunc(); //线程运行函数
EventLoop* loop_; //指向一个EventLoop对象,一个I/O线程有且只有一个EventLoop对象
bool exiting_;
Thread thread_; //基于对象,包含了一个thread类对象
MutexLock mutex_;
Condition cond_;
ThreadInitCallback callback_; //回调函数在EventLoop::loop事件循环之前被调用
};
}
}
#endif // MUDUO_NET_EVENTLOOPTHREAD_H
实现文件:
#include
#include
#include
using namespace muduo;
using namespace muduo::net;
EventLoopThread::EventLoopThread(const ThreadInitCallback& cb,
const string& name)
: loop_(NULL), //loop未启动为NULL
exiting_(false),
thread_(boost::bind(&EventLoopThread::threadFunc, this), name), //绑定线程运行函数
mutex_(),
cond_(mutex_),
callback_(cb) //初始化回调函数
{
}
EventLoopThread::~EventLoopThread()
{
exiting_ = true;
if (loop_ != NULL) // not 100% race-free, eg. threadFunc could be running callback_.
{
// still a tiny chance to call destructed object, if threadFunc exits just now.
// but when EventLoopThread destructs, usually programming is exiting anyway.
loop_->quit(); //退出I/O线程,让I/O线程的loop循环退出,从而退出了I/O线程
thread_.join();
}
}
//启动EventLoopThread中的loop循环,内部实际调用thread_.start
EventLoop* EventLoopThread::startLoop()
{
assert(!thread_.started());
thread_.start();
{
MutexLockGuard lock(mutex_);
while (loop_ == NULL)
{
cond_.wait();
}
}
return loop_;
}
//该函数是EventLoopThread类的核心函数,作用是启动loop循环
//该函数和上面的startLoop函数并发执行,所以需要上锁和condition
void EventLoopThread::threadFunc()
{
EventLoop loop;
if (callback_)
{
callback_(&loop); //构造函数传递进来的,线程启动执行回调函数
}
{
MutexLockGuard lock(mutex_);
loop_ = &loop; //然后loop_指针指向了这个创建的栈上的对象,threadFunc退出之后,这个指针就失效了
cond_.notify(); //该函数退出,意味着线程就推出了,EventLoopThread对象也就没有存在的价值了。但是muduo的EventLoopThread
//实现为自动销毁的。一般loop函数退出整个程序就退出了,因而不会有什么大的问题,
//因为muduo库的线程池就是启动时分配,并没有释放。所以线程结束一般来说就是整个程序结束了。
}
loop.loop(); //开始loop循环
//assert(exiting_);
loop_ = NULL;
}
这个类要注意的就是最后的这个两函数了,下面来分别说一下:
EventLoop* EventLoopThread::startLoop()
{
assert(!thread_.started());
thread_.start();
{
MutexLockGuard lock(mutex_);
while (loop_ == NULL)
{
cond_.wait();
}
}
return loop_;
}
这个函数是启动EventLoopThread类所包含的线程thread_,可以理解为启动worker线程,启动了之后该函数一直因condition不满足等待这里,该函数的作用是启动EventLoopThread中的线程去启动EventLoop,然后把EventLoop对象的指针返给调用者,由于启动线程,它和thread_的worker线程threadFunc是并发执行的,所以它需要等待loop产生成功才能返回loop的指针。loop是I/O线程最重要的循环不必多说,它是事件处理的核心。
那就看一下thread_的线程运行函数,知道thread_.start()之后,运行函数帮我们做了什么:
//该函数是EventLoopThread类的核心函数,作用是启动loop循环
//该函数和上面的startLoop函数并发执行,所以需要上锁和condition
void EventLoopThread::threadFunc()
{
EventLoop loop;
if (callback_)
{
callback_(&loop); //构造函数传递进来的,线程启动执行回调函数
}
{
MutexLockGuard lock(mutex_);
loop_ = &loop; //然后loop_指针指向了这个创建的栈上的对象,threadFunc退出之后,这个指针就失效了
cond_.notify(); //该函数退出,意味着线程就推出了,EventLoopThread对象也就没有存在的价值了。但是muduo的EventLoopThread
//实现为自动销毁的。一般loop函数退出整个程序就退出了,因而不会有什么大的问题,
//因为muduo库的线程池就是启动时分配,并没有释放。所以线程结束一般来说就是整个程序结束了。
}
loop.loop(); //开始loop循环
//assert(exiting_);
loop_ = NULL;
}
这就是EventLoopThread类工作核心,如果EventLoopThread类构造时注册了回调函数,这个时候还可以先执行回调函数。
我们发现了一句重要的语句:
EventLoop loop;
它在栈上声明了一个EventLoop对象loop,接下来又用loop_指针指向该对象,然后开始loop.loop()循环。当我们loop()结束时,把它赋为空。
我们发现loop()结束后,线程也就结束了,那么EventLoopThread对象已经没有存在的价值了。但是并没有实现立即销毁EventLoopThread。
这是因为该函数退出,意味着线程就退出了,EventLoopThread对象也就没有存在的价值了。但是muduo的EventLoopThread实现为自动销毁的。一般loop函数退出整个程序就退出了,因而不会有什么大的问题。因为muduo库的线程池就是启动时分配,并没有释放。所以线程结束一般来说就是整个程序结束了。
下面是一个测试例子:
#include
#include
#include
using namespace muduo;
using namespace muduo::net;
void th_fn()
{
printf("th_fn() : pid = %d, tid = %d\n", getpid(), CurrentThread::tid());
}
int main()
{
printf("main(): pid = %d, tid = %d\n",
getpid(), CurrentThread::tid());
EventLoopThread loopThread; //create a EventLoopThread object.
/*EventLoop* EventLoopThread::EventLoopThread::startLoop()*/
EventLoop* loop = loopThread.startLoop();
//异步调用runInThread,即将runInThread添加到loop对象所在的I/O线程,让该I/O线程执行
loop->runInLoop((th_fn));
sleep(1);
//runAfter内部也调用了runInLoop,所以这里也是异步调用
loop->runAfter(2, th_fn);
sleep(3);
loop->quit();
printf("exit main().\n");
return 0;
}
编译选项要注意,muduo的静态链接库可能没有包含全部,所以加上编译具体的.cpp:
g++ -o main /usr/include/muduo/net/TimerQueue.cc /usr/include/muduo/net/EventLoop.cc event_loop_thread.cpp -lmuduo_net -lmuduo_base -lpthread
输出: