muduo网络库源码复现笔记(二十):EventLoop::runInloop()函数和EventLoopThread类

Muduo网络库简介

muduo 是一个基于 Reactor 模式的现代 C++ 网络库,作者陈硕。它采用非阻塞 IO 模型,基于事件驱动和回调,原生支持多核多线程,适合编写 Linux 服务端多线程网络应用程序。
muduo网络库的核心代码只有数千行,在网络编程技术学习的进阶阶段,muduo是一个非常值得学习的开源库。目前我也是刚刚开始学习这个网络库的源码,希望将这个学习过程记录下来。这个网络库的源码已经发布在GitHub上,可以点击这里阅读。目前Github上这份源码已经被作者用c++11重写,我学习的版本是没有使用c++11版本的。不过二者大同小异,核心思想是没有变化的。点这里可以看我的源代码。从笔记十七开始记录muduo的net库的实现过程。如果你需要看一下基础库(base)的复现过程,可以点击这里:muduo的base库实现过程。而网络库的笔记在这里:
muduo网络库源码复现笔记(十七):什么都不做的EventLoop
muduo网络库源码复现笔记(十八):Reactor的关键结构
muduo网络库源码复现笔记(十九):TimeQueue定时器

1 EventLoop::runInLoop函数

EventLoop的runInLoop函数实现了在IO线程内执行用户回调的功能。如代码所示,它接受一个任务回调函数cb(boost::function<void main>)。如果在当前IO线程调用这个函数,这个任务会直接执行,否则会加入队伍,唤醒IO线程来执行。

void EventLoop::runInLoop(const Functor& cb)
{
    
    
	if(isInLoopThread())
	{
    
    
		cb();
	}
	else
	{
    
    
		queueInLoop(cb);
	}
}

这个函数实现了线程之间的任务调配。比如说我们在IO线程之外的线程中,若有一个EventLoop* g_loop, 使用g_loop -> runInLoop(task),使task在IO线程中执行。
queueInLoop也不难,首先我们使用加锁的方式将cb放入一个Functor的容器中,然后根据条件判定是否需要唤醒IO线程。因为IO线程有可能正在阻塞在poll中。这个条件是:(1)在非IO线程中执行了queueInLoop,这个好理解,因为IO线程有可能正在阻塞在poll中。(2)正在调用容器中的cb,这时我们唤醒的目的在于为poll中的文件描述符写入事件,至于为什么是这样,是由wakeup的实现原理决定的。

void EventLoop::queueInLoop(const Functor& cb)
{
    
    
	{
    
    
		MutexLockGuard lock(mutex_);
		pendingFunctors_.push_back(cb);
	}
	if(!isInLoopThread() || callingPendingFunctors_)
	{
    
    
		wakeup();
	}
}

wakeup的实现是使用了eventFd,在EventLoop初始化时将它加入poller,如果需要唤醒,只需对eventFd写入信息使poller监听到即可。

void EventLoop::wakeup()
{
	uint64_t one = 1;
	//ssize_t n = sockets::write(wakeupFd_,&one,sizeof one);
	ssize_t n = ::write(wakeupFd_,&one,sizeof one);
	if(n != sizeof one)
	{
		LOG_ERROR << "EventLoop::wakeup() writes " << n << "bytes instead of 8";
	}
}

2 EventLoopThread class

拥有EventLoop的线程不一定是主线程,事实上,我们可以在任意线程创建EventLoop,使他成为IO线程。EvntLoopThread类实现了这样一个功能。它使用了base库的Thread。这个类的重点是startLoop和threadFunc两个函数。

class EventLoopThread : boost::noncopyable
{
    
    
public:
	typedef boost::function<void(EventLoop*)> ThreadInitCallback;
	
	EventLoopThread(const ThreadInitCallback& cb = ThreadInitCallback());
	~EventLoopThread();
	EventLoop* startLoop(); //返回EventLoop的指针

private:
	void threadFunc();//thread_的逻辑函数
	
	EventLoop* loop_;
	bool exiting_;
	Thread thread_;
	MutexLock mutex_;
	Condition cond_;
	ThreadInitCallback callback_;	

2.1 startLoop函数

这个函数是启动线程,并返回线程中EventLoop的指针。函数里使用了条件变量来等待线程的创建。线程start中执行的逻辑如前所述就是threadFunc。

EventLoop* EventLoopThread::startLoop()
{
    
    
	assert(!thread_.started());
	thread_.start();
	{
    
    
		MutexLockGuard lock(mutex_);
		while(loop_ == NULL)
		{
    
    
			cond_.wait();
		}
	}
	return loop_;
}

2.2 threadFunc函数

threadFunc中创建一个EventLopp,赋值给&loop,然后唤醒startLoop,然后执行loop函数。

void EventLoopThread::threadFunc()
{
    
    
	EventLoop loop;
	if(callback_)
	{
    
    
		callback_(&loop);
	}

	{
    
    
		MutexLockGuard lock(mutex_);
		loop_ = &loop;
		cond_.notify();		
	}
	loop.loop();
}

猜你喜欢

转载自blog.csdn.net/MoonWisher_liang/article/details/107522190
今日推荐