知识点:Linux多线程服务器端编程 8.0 8.1
最近在看陈硕的用C++写的muduo库,学习服务器编程。
EventLoop的作用是保证一个线程,当然后面有会讲如何实现多线程编程。
Channel负责一个事件,包括文件描述符,回调函数,个人认为可以再加上回调函数 参数,以及函数调用结果保存参数,这样子就是一个完美的事件封装。
Poller负责持有所有的channel事件以及有io的active channel事件更新,当调用poll时,检测所有的channel事件是否有活动,poller类对于使用者来说是不可见的。
那么整个程序的过程如下:
用户注册一个channel事件,传入事件描述符和回调函数,其实应该还可以传入参数,把事件加入poller类中的channel事件库(通过enableReading()函数实现),
然后Eventloop中loop函数中启动poll检测,阻塞在这里。当有io准备就绪时,更新active channel事件,然后执行active channel事件的回调函数(handleEvent()函数)。
代码如下,我自己运行都是可以通过的。
#ifndef EVENTLOOP_H
#define EVENTLOOP_H
#include <vector>
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>
#include "muduo/base/Thread.h"
#include "muduo/base/CurrentThread.h"
namespace muduo{
class Channel;
class Poller;
class EventLoop :boost::noncopyable
{
public:
EventLoop();
~EventLoop();
void loop();
void quit();
void assertInLoopThread()
{
if (!isInLoopThread())
{
abortNotInLoopThread();
}
}
bool isInLoopThread()const{
return threadId_ == muduo::CurrentThread::tid();
}
EventLoop* getEventLoopOfCurrentThread();
void updateChannel(Channel* channel);
private:
void abortNotInLoopThread();
typedef std::vector<Channel*>ChannelList;
bool looping_;
bool quit_;
const pid_t threadId_;
boost::scoped_ptr<Poller> poller_;
ChannelList activeChannels_;
};
}
#endif // EVENTLOOP_H
#include "eventloop.h"
#include <muduo/base/Logging.h>
#include "channel.h"
#include "poller.h"
#include<assert.h>
using namespace muduo;
__thread EventLoop* t_loopInThisThread = 0 ;
const int kPollTimeMs = 10000;
EventLoop::EventLoop():looping_(false),threadId_( CurrentThread::tid()),poller_(new Poller(this))
{
LOG_TRACE<<"EventLoop created"<<this<<"in thread"<<threadId_;
if (t_loopInThisThread)
{
LOG_FATAL<<"Another EventLoop"<<t_loopInThisThread<<"exists in this thread"<<threadId_;
}
else
{
t_loopInThisThread = this;
}
}
EventLoop::~EventLoop()
{
assert(!looping_);
t_loopInThisThread = NULL;
}
void EventLoop::loop()
{
assert(!looping_);
assertInLoopThread();
looping_ = true;
quit_ = false;
while (!quit_)
{
activeChannels_.clear();
poller_->poll(kPollTimeMs, &activeChannels_);
for (ChannelList::iterator it = activeChannels_.begin();
it != activeChannels_.end(); ++it)
{
(*it)->handleEvent();
}
}
LOG_TRACE << "EventLoop " << this << " stop looping";
looping_ = false;
}
void EventLoop::quit()
{
quit_ = true;
// wakeup();
}
EventLoop *EventLoop::getEventLoopOfCurrentThread()
{
return t_loopInThisThread;
}
void EventLoop::updateChannel(Channel* channel)
{
assert(channel->ownerLoop() == this);
assertInLoopThread();
poller_->updateChannel(channel);
}
void EventLoop::abortNotInLoopThread()
{
;
}
#ifndef CHANNEL_H
#define CHANNEL_H
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
namespace muduo{
class EventLoop;
///
/// A selectable I/O channel.
///
/// This class doesn't own the file descriptor.
/// The file descriptor could be a socket,
/// an eventfd, a timerfd, or a signalfd
class Channel : boost::noncopyable
{
public:
typedef boost::function<void()> EventCallback;
Channel(EventLoop* loop, int fd);
void handleEvent();
void setReadCallback(const EventCallback& cb)
{ readCallback_ = cb; }
void setWriteCallback(const EventCallback& cb)
{ writeCallback_ = cb; }
void setErrorCallback(const EventCallback& cb)
{ errorCallback_ = cb; }
int fd() const { return fd_; }
int events() const { return events_; }
void set_revents(int revt) { revents_ = revt; }
bool isNoneEvent() const { return events_ == kNoneEvent; }
void enableReading() { events_ |= kReadEvent; update(); }
// void enableWriting() { events_ |= kWriteEvent; update(); }
// void disableWriting() { events_ &= ~kWriteEvent; update(); }
// void disableAll() { events_ = kNoneEvent; update(); }
// for Poller
int index() { return index_; }
void set_index(int idx) { index_ = idx; }
EventLoop* ownerLoop() { return loop_; }
private:
void update();
static const int kNoneEvent;
static const int kReadEvent;
static const int kWriteEvent;
EventLoop* loop_;
const int fd_;
int events_;
int revents_;
int index_; // used by Poller.
EventCallback readCallback_;
EventCallback writeCallback_;
EventCallback errorCallback_;
};
}
#endif // CHANNEL_H
#include "channel.h"
#include <poll.h>
#include <muduo/base/Logging.h>
#include "eventloop.h"
#include<sstream>
using namespace muduo;
const int Channel::kNoneEvent = 0;
const int Channel::kReadEvent = POLLIN | POLLPRI;
const int Channel::kWriteEvent = POLLOUT;
Channel::Channel(EventLoop *loop, int fd)
:loop_(loop),
fd_(fd),
events_(0),
revents_(0),
index_(-1)
{
}
void Channel::handleEvent()
{
if (revents_ & POLLNVAL) {
LOG_WARN << "Channel::handle_event() POLLNVAL";
}
if (revents_ & (POLLERR | POLLNVAL)) {
if (errorCallback_) errorCallback_();
}
if (revents_ & (POLLIN | POLLPRI | POLLRDHUP)) {
if (readCallback_) readCallback_();
}
if (revents_ & POLLOUT) {
if (writeCallback_) writeCallback_();
}
}
void Channel::update()
{
loop_->updateChannel(this);
}
#ifndef POLLER_H
#define POLLER_H
#include <map>
#include <vector>
#include <boost/noncopyable.hpp>
#include <muduo/base/Timestamp.h>
#include "eventloop.h"
struct pollfd;
namespace muduo{
class Channel;
//class EventLoop;
class Poller :boost::noncopyable
{
public:
typedef std::vector<Channel*>ChannelList;
Poller(EventLoop* loop);
~Poller();
/// Polls the I/O events.
/// Must be called in the loop thread.
Timestamp poll(int timeoutMs, ChannelList* activeChannels);
/// Changes the interested I/O events.
/// Must be called in the loop thread.
void updateChannel(Channel* channel);
void assertInLoopThread() { ownerLoop_->assertInLoopThread(); }
private:
void fillActiveChannels(int numEvents,
ChannelList* activeChannels) const;
typedef std::vector<struct pollfd> PollFdList;
typedef std::map<int, Channel*>ChannelMap;
EventLoop* ownerLoop_;
PollFdList pollfds_;
ChannelMap channels_;
};
}
#endif // POLLER_H
#include <iostream>
#include<stdio.h>
#include"eventloop.h"
#include "channel.h"
using namespace std;
using namespace muduo;
/*
void threadFunc()
{
printf("threadFunc(): pid = %d, tid = %d\n", getpid(), CurrentThread::tid());
EventLoop loop;
loop.loop();
}
int main()
{
cout << "Hello World!" << endl;
printf("threadFunc(): pid = %d, tid = %d\n", getpid(), CurrentThread::tid());
EventLoop loop;
muduo::Thread thread(threadFunc);
thread.start();
loop.loop();
pthread_exit(NULL);
return 0;
}
*/
/*
muduo::EventLoop* g_loop;
void threadFunc()
{
g_loop->loop();
}
int main()
{
muduo::EventLoop loop;
g_loop = &loop;
muduo::Thread t(threadFunc);
t.start();
t.join();
}
*/
/*
#include<sys/timerfd.h>
EventLoop* g_loop;
void timeout()
{
printf("timeout!\n");
g_loop->quit();
}
int main()
{
EventLoop loop;
int timerfd = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
muduo::Channel channel(&loop, timerfd);
channel.setReadCallback(timeout);
channel.enableReading();
struct itimerspec howlong;
bzero(&howlong, sizeof howlong);
howlong.it_value.tv_sec = 5;
::timerfd_settime(timerfd, 0, &howlong, NULL);
loop.loop();
::close(timerfd);
}
*/
我使用qt来编写的,工程文件要加入头文件。TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt
INCLUDEPATH += /home/myubuntu/build/release-install/include/
LIBS += -L /home/myubuntu/build/release-install/lib -lmuduo_net -lmuduo_base
LIBS += -lpthread
SOURCES += main.cpp \
eventloop.cpp \
channel.cpp \
poller.cpp
HEADERS += \
eventloop.h \
channel.h \
poller.h