进程(线程)wait/notify
pipe
socketpair
eventfd
eventfd 是一个比 pipe 更高效的线程间事件通知机制,一方面它比 pipe 少用一个 file descripor,节省了资源;另一方面,eventfd 的缓冲区管理也简单得多,全部“buffer” 只有定长8 bytes,不像 pipe 那样可能有不定长的真正 buffer。
eventloop.h
这里的eventloop 和 “muduo_net库源码分析(26-1)”里面的内容有些不一样,这里的eventloop加了一些内容
在阅读eventloop时,要注意runInLoop和queueInloop这两个函数,wakeup()也得注意
// Copyright 2010, Shuo Chen. All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
// Author: Shuo Chen (chenshuo at chenshuo dot com)
//
// This is a public header file, it must only include public header files.
#ifndef MUDUO_NET_EVENTLOOP_H
#define MUDUO_NET_EVENTLOOP_H
#include
#include
#include
#include
#include
#include
#include
#include
namespace muduo
{
namespace net
{
class Channel;
class Poller;
class TimerQueue;
///
/// Reactor, at most one per thread.
///
/// This is an interface class, so don't expose too much details.
class EventLoop : boost::noncopyable
{
public:
/* IO事件回调函数,就像“监听socketfd” ,我们可以把 监听socketfd 的处理函数 做成一个回调函数,
把这个函数放到pendingFunctors_中,然后由IO线程自己回调处理*/
typedef boost::function Functor;
EventLoop();
~EventLoop(); // force out-line dtor, for scoped_ptr members.
///
/// Loops forever.
///
/// Must be called in the same thread as creation of the object.
///
void loop();
void quit();
///
/// Time when poll returns, usually means data arrivial.
///
Timestamp pollReturnTime() const { return pollReturnTime_; }
/// Runs callback immediately in the loop thread.
/// It wakes up the loop, and run the cb.
/// If in the same loop thread, cb is run within the function.
/// Safe to call from other threads.
void runInLoop(const Functor& cb);
/// Queues callback in the loop thread.
/// Runs after finish pooling.
/// Safe to call from other threads.
void queueInLoop(const Functor& cb);
// timers
///
/// Runs callback at 'time'.
/// Safe to call from other threads.
///
TimerId runAt(const Timestamp& time, const TimerCallback& cb);
///
/// Runs callback after @c delay seconds.
/// Safe to call from other threads.
///
TimerId runAfter(double delay, const TimerCallback& cb);
///
/// Runs callback every @c interval seconds.
/// Safe to call from other threads.
///
TimerId runEvery(double interval, const TimerCallback& cb);
///
/// Cancels the timer.
/// Safe to call from other threads.
///
void cancel(TimerId timerId);
// internal usage
void wakeup();
void updateChannel(Channel* channel); // 在Poller中添加或者更新通道
void removeChannel(Channel* channel); // 从Poller中移除通道
void assertInLoopThread()
{
if (!isInLoopThread())
{
abortNotInLoopThread();
}
}
bool isInLoopThread() const { return threadId_ == CurrentThread::tid(); }
bool eventHandling() const { return eventHandling_; }
static EventLoop* getEventLoopOfCurrentThread();
private:
void abortNotInLoopThread();
void handleRead(); // waked up
void doPendingFunctors();
void printActiveChannels() const; // DEBUG
typedef std::vector ChannelList;
bool looping_; /* atomic */
bool quit_; /* atomic */
bool eventHandling_; /*事件处理函数状态 atomic */
bool callingPendingFunctors_; /*是否处于IO回调函数的处理状态中 atomic */
const pid_t threadId_; // 当前对象所属线程ID
Timestamp pollReturnTime_;
boost::scoped_ptr poller_;
boost::scoped_ptr timerQueue_;
/*****
wakeupFd_ : 这个描述符主要是未了
****/
int wakeupFd_; // 用于eventfd所创建的文件描述符
// unlike in TimerQueue, which is an internal class,
// we don't expose Channel to client.
/*
wakeupChannel 和EventLoop 是组合的关系,生命周期由EventLoop管理
一定要看下面的内容,如果不明白下面的内容的话,代码是很难懂的(个人观点,高手勿喷!!^V^)
当其他线程跟IO线程通信时, 他们使用的是同一个通道(wakeupChannel_) , 当其他线程wakeup(write)时,
poll会返回。然后IO执行handleRead,但是主要的回调函数不是handleRead ,而是pendingFunctors_里面的函数,
hadnleRead只是为了不被多次触发而已,也就是说其他线程要想IO线程执行他们所需的函调函数,只要把回调函数
注入到pendingFunctors_,然后wakeup(write)唤醒一下。
当然IO线程也可以和IO线程进行通信,但是情况有些不一样,具体哪些不同,大家自己看着办吧!
注: IO线程就是EventLoop所属的线程
*/
boost::scoped_ptr wakeupChannel_; //wakeupFd_所对应的通道 该通道将会纳入poller_来管理
ChannelList activeChannels_; // Poller返回的活动通道
Channel* currentActiveChannel_; // 当前正在处理的活动通道
MutexLock mutex_;
// IO线程 回调函数容器
std::vector pendingFunctors_; // @BuardedBy mutex_
};
}
}
#endif // MUDUO_NET_EVENTLOOP_H
这里的eventloop 和 “muduo_net库源码分析(26-1)”里面的内容有些不一样,这里的eventloop加了一些内容,具体情况可以回去看一看
// Copyright 2010, Shuo Chen. All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
// Author: Shuo Chen (chenshuo at chenshuo dot com)
#include
#include
#include
#include
#include
//#include
#include
#include
using namespace muduo;
using namespace muduo::net;
namespace
{
// 当前线程EventLoop对象指针
// 线程局部存储
__thread EventLoop* t_loopInThisThread = 0;
const int kPollTimeMs = 10000;
int createEventfd()
{
int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
if (evtfd < 0)
{
LOG_SYSERR << "Failed in eventfd";
abort();
}
return evtfd;
}
}
EventLoop* EventLoop::getEventLoopOfCurrentThread()
{
return t_loopInThisThread;
}
EventLoop::EventLoop()
: looping_(false),
quit_(false),
eventHandling_(false),
callingPendingFunctors_(false),
threadId_(CurrentThread::tid()),
poller_(Poller::newDefaultPoller(this)),
timerQueue_(new TimerQueue(this)),
wakeupFd_(createEventfd()), //创建 wakeupFd_
wakeupChannel_(new Channel(this, wakeupFd_)), //创建wakeupFd_对应的channel
currentActiveChannel_(NULL)
{
LOG_TRACE << "EventLoop created " << this << " in thread " << threadId_;
// 如果当前线程已经创建了EventLoop对象,终止(LOG_FATAL)
if (t_loopInThisThread)
{
LOG_FATAL << "Another EventLoop " << t_loopInThisThread
<< " exists in this thread " << threadId_;
}
else
{
t_loopInThisThread = this;
}
// 设定wakeupChannel的回调函数
wakeupChannel_->setReadCallback(
boost::bind(&EventLoop::handleRead, this));
// we are always reading the wakeupfd
//纳入poller来管理
wakeupChannel_->enableReading();
}
EventLoop::~EventLoop()
{
::close(wakeupFd_);
t_loopInThisThread = NULL;
}
// 事件循环,该函数不能跨线程调用
// 只能在创建该对象的线程中调用
void EventLoop::loop()
{
assert(!looping_);
// 断言当前处于创建该对象的线程中
assertInLoopThread();
looping_ = true;
quit_ = false;
LOG_TRACE << "EventLoop " << this << " start looping";
//::poll(NULL, 0, 5*1000);
while (!quit_)
{
activeChannels_.clear();
pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);
//++iteration_;
if (Logger::logLevel() <= Logger::TRACE)
{
printActiveChannels();
}
// TODO sort channel by priority
eventHandling_ = true;
for (ChannelList::iterator it = activeChannels_.begin();
it != activeChannels_.end(); ++it)
{
currentActiveChannel_ = *it;
currentActiveChannel_->handleEvent(pollReturnTime_);
}
currentActiveChannel_ = NULL;
eventHandling_ = false;
// 让IO线程也能执行一些计算任务
doPendingFunctors();
}
LOG_TRACE << "EventLoop " << this << " stop looping";
looping_ = false;
}
// 该函数可以跨线程调用
void EventLoop::quit()
{
quit_ = true;
//如果不是由当前IO线程调用的,那么我们还要唤醒IO线程
if (!isInLoopThread())
{
wakeup();
}
}
/*
sublime 注释的好方法,都是苦力活
=========== =================== ====== ===================
|| ||| |||================= = {}
|| **** || = {}
|| **** || = {}
||||||||| ||================= = = = = {}
|| \\\ ||================== /// {}
|| \\ || = // {}
// // {}
|| \\\ || ================== // {}
|| \\ ||=================== ========= {}
*/
// 在I/O线程中注册某个回调函数,该函数可以跨线程调用
void EventLoop::runInLoop(const Functor& cb)
{
if (isInLoopThread())
{
// 如果是当前IO线程调用runInLoop,则同步调用cb
cb();
}
else
{
// 如果是其它线程调用runInLoop,则异步地将cb添加到队列,让EventLoop线程来执行这个cb()函数
queueInLoop(cb);
}
}
// 把IO回调函数注册到pendingFunctors_中
void EventLoop::queueInLoop(const Functor& cb)
{
{
// 保护临界区
MutexLockGuard lock(mutex_);
// 将任务添加到任务队列中
pendingFunctors_.push_back(cb);
}
// 1.--->调用queueInLoop的线程不是IO线程需要唤醒,以便IO线程及时处理任务
// 2.--->或者调用queueInLoop的线程是IO线程,并且此时正在调用pending functor,需要唤醒
/* 如果我们不唤醒,那么下一次poll就没法获取该cb的触发事件,这样就会使事件处理延迟了 ,请参照loop函数 */
// 3---->只有IO线程的事件回调中调用queueInLoop才不需要唤醒 ,因为handleEvent执行后,
//下面接着执行doPendingFunctors
if (!isInLoopThread() || callingPendingFunctors_)
{
wakeup();
}
}
TimerId EventLoop::runAt(const Timestamp& time, const TimerCallback& cb)
{
return timerQueue_->addTimer(cb, time, 0.0);
}
TimerId EventLoop::runAfter(double delay, const TimerCallback& cb)
{
Timestamp time(addTime(Timestamp::now(), delay));
return runAt(time, cb);
}
TimerId EventLoop::runEvery(double interval, const TimerCallback& cb)
{
Timestamp time(addTime(Timestamp::now(), interval));
return timerQueue_->addTimer(cb, time, interval);
}
void EventLoop::cancel(TimerId timerId)
{
return timerQueue_->cancel(timerId);
}
void EventLoop::updateChannel(Channel* channel)
{
assert(channel->ownerLoop() == this);
assertInLoopThread();
poller_->updateChannel(channel);
}
void EventLoop::removeChannel(Channel* channel)
{
assert(channel->ownerLoop() == this);
assertInLoopThread();
if (eventHandling_)
{
assert(currentActiveChannel_ == channel ||
std::find(activeChannels_.begin(), activeChannels_.end(), channel) == activeChannels_.end());
}
poller_->removeChannel(channel);
}
void EventLoop::abortNotInLoopThread()
{
LOG_FATAL << "EventLoop::abortNotInLoopThread - EventLoop " << this
<< " was created in threadId_ = " << threadId_
<< ", current thread id = " << CurrentThread::tid();
}
/*
唤醒等待的线程
*/
void EventLoop::wakeup()
{
uint64_t one = 1;
//ssize_t n = sockets::write(wakeupFd_, &one, sizeof one);
/*向wakeupFd_中写入数据*/
ssize_t n = ::write(wakeupFd_, &one, sizeof one);
if (n != sizeof one)
{
LOG_ERROR << "EventLoop::wakeup() writes " << n << " bytes instead of 8";
}
}
// IO事件回调函数
void EventLoop::handleRead()
{
uint64_t one = 1;
//ssize_t n = sockets::read(wakeupFd_, &one, sizeof one);--->>muduolib
ssize_t n = ::read(wakeupFd_, &one, sizeof one); // 这是先这样设定
if (n != sizeof one)
{
LOG_ERROR << "EventLoop::handleRead() reads " << n << " bytes instead of 8";
}
}
/*IO线程的回调函数集 处理函数*/
void EventLoop::doPendingFunctors()
{
std::vector functors;
/*正在处于IO线程处理函数中*/
callingPendingFunctors_ = true;
{
// 加锁
MutexLockGuard lock(mutex_);
//交换,顺带将pendingFunctors清空
functors.swap(pendingFunctors_);
}
for (size_t i = 0; i < functors.size(); ++i)
{
functors[i]();
}
/*清空IO线程回调处理函数中 的状态*/
callingPendingFunctors_ = false;
}
void EventLoop::printActiveChannels() const
{
for (ChannelList::const_iterator it = activeChannels_.begin();
it != activeChannels_.end(); ++it)
{
const Channel* ch = *it;
LOG_TRACE << "{" << ch->reventsToString() << "} ";
}
}
#include
//#include
//#include
#include
using namespace muduo;
using namespace muduo::net;
EventLoop* g_loop;
int g_flag = 0;
void run4()
{
printf("run4(): pid = %d, flag = %d\n", getpid(), g_flag);
g_loop->quit();
}
void run3()
{
printf("run3(): pid = %d, flag = %d\n", getpid(), g_flag);
g_loop->runAfter(3, run4);
g_flag = 3;
}
void run2()
{
printf("run2(): pid = %d, flag = %d\n", getpid(), g_flag);
/*在IO线程中直接调用queueInLoop,并且if (!isInLoopThread() || callingPendingFunctors_)时,
不用唤醒(wakeup())
*/
g_loop->queueInLoop(run3);
}
void run1()
{
g_flag = 1;
printf("run1(): pid = %d, flag = %d\n", getpid(), g_flag);
/*在IO线程中,调用runInLoop时,不用经过queueInLoop()就可以直接调用Functors*/
g_loop->runInLoop(run2);
g_flag = 2;
}
int main()
{
printf("main(): pid = %d, flag = %d\n", getpid(), g_flag);
EventLoop loop;
g_loop = &loop;
loop.runAfter(2, run1);
loop.loop();
printf("main(): pid = %d, flag = %d\n", getpid(), g_flag);
}
程序输出
ubuntu@ubuntu-virtual-machine:~/29/jmuduo$ ../build/debug/bin/reactor_test05
main(): pid = 28193, flag = 0
20131022 01:36:15.466526Z 28193 TRACE updateChannel fd = 4 events = 3 - EPollPoller.cc:104
20131022 01:36:15.467517Z 28193 TRACE EventLoop EventLoop created 0xBFEDB874 in thread 28193 - EventLoop.cc:62
20131022 01:36:15.467586Z 28193 TRACE updateChannel fd = 5 events = 3 - EPollPoller.cc:104
20131022 01:36:15.467898Z 28193 TRACE loop EventLoop 0xBFEDB874 start looping - EventLoop.cc:94
20131022 01:36:17.469365Z 28193 TRACE poll 1 events happended - EPollPoller.cc:65
20131022 01:36:17.482824Z 28193 TRACE printActiveChannels {4: IN } - EventLoop.cc:257
20131022 01:36:17.483170Z 28193 TRACE readTimerfd TimerQueue::handleRead() 1 at 1382405777.482909 - TimerQueue.cc:62
run1(): pid = 28193, flag = 1
run2(): pid = 28193, flag = 1
run3(): pid = 28193, flag = 2
20131022 01:36:20.483953Z 28193 TRACE poll 1 events happended - EPollPoller.cc:65
20131022 01:36:20.484074Z 28193 TRACE printActiveChannels {4: IN } - EventLoop.cc:257
20131022 01:36:20.484119Z 28193 TRACE readTimerfd TimerQueue::handleRead() 1 at 1382405780.484109 - TimerQueue.cc:62
run4(): pid = 28193, flag = 3
20131022 01:36:20.484229Z 28193 TRACE loop EventLoop 0xBFEDB874 stop looping - EventLoop.cc:119
main(): pid = 28193, flag = 3
ubuntu@ubuntu-virtual-machine:~/29/jmuduo$