源码下载以及安装点击链接https://blog.youkuaiyun.com/YoungSusie/article/details/90021742
Reactor 最核心的 事件分发机制,即将IO复用拿到的IO事件分发给各个对应的文件描述符的事件处理函数。
每个Channel对象自始自终只属于一个Event Loop,因此每个Channel 对象只属于一个IO线程。 每个Channel 对象自始自终只负责一个文件描述符的IO事件分发, 但是Channel 对象并不拥有这个文件描述符,不会在析构的时候关闭这个文件描述符。 Channel 会将不同的IO事件分发为不同的回调,例如 Readcallback 、 writecallback 等,回调采用的是 boost::function,用通用的函数模板,实现不同fd的事件回调函数 表示,用户无需继承Channel, Channel不是基类。
1、学习笔记
- boost::function 和 boost::bind 头文件<booost/function.hpp>
boost.function 库用来提供一个对象化的函数指针。这个对象用于封装另一个函数。
boost::function 是一个函数包装器(也叫函数模板) ,可以用来代替拥有相同返回类型,相同参数类型,以及相同参数个数的各个不同的函数。函数的具体实现可以根据需要改变,类似C++中的多态。
typedef boost::function<int(int,char)> fun; //函数返回值为int ,参数分别为int char
fun f;
f = &test;//test 为具体实现的函数名。
当function f 首次创建时,不保存任何函数,是空的。不可调用一个没有保存任何函数或者函数对象的 function,会抛出错误。 需要将另外的函数的指针赋值给 f 。
Boost.Function 不支持参数绑定,这在每次调用一个function 就要调用同一个类实例的成员函数时是需要的。幸运的是,如果这个类实例被传递给function的话,可以直接调用它的成员函数。这个function 必须包含类的类型以及成员函数的签名。换言之,显示传入的 类实例要作为隐式传递的第一个参数,this。
- 传递值
class some_class {
public:
void do_stuff(int i) const {
std::cout << "OK. Stuff is done. " << i << '/n';
}
};
如果成员函数 do_stuff要从一个boost::function 实例里被调用,需要function接受一个some_class实例,签名的其他部分为一个void 返回以及一个int参数。
boost::function<void(some_class,int)> f;
调用的时候:
f=&some_class::do_stuff;
f(some_class(),2);
- 传递引用
boost::function<void(some_class&,int)> f;
f=&some_class::do_stuff;
some_class s;
f(s,1);
- 传递指针
boost::function<void(some_class*,int)> f;
f=&some_class::do_stuff;
some_class s;
f(&s,3);
- 与Boost.Function 一起使用 Boost.Bind
Boost.Bind 为普通函数、成员函数以及成员变量提供参数绑定,非常适合于Boost.Function 。 由于我们使用的类本身并不是函数对象,那么,可以用Boost.Bind 将它们转变为函数对象,然后用Boost.Function 来保存以便稍后调用。
区别于基于函数指针 (typedef void (*function_type)(int); )的事件回调,基于函数指针的事件回调,回调类型收到限制。
通过使用 Boost.Function,我们可以在当与某个支持参数绑定的库结合使用时,轻而易举地把上下文提供给调用的函数。这是本库最常见的用途之一,把业务逻辑从表示层分离出来。
class button
{
public:
boost::function<void()> onClick;
};
class player
{
public:
void play();
void stop();
};
button playButton, stopButton;
player thePlayer;
void connect()
{
playButton.onClick = boost::bind(&player::play, &thePlayer); //
stopButton.onClick = boost::bind(&player::stop, &thePlayer);
}
- muduo项目中需要考虑的问题
需要考虑的问题: 不同的IO事件回调需要处理不同的任务,这些任务的返回值类型,参数类型,参数个数不一定相同,如何像上面一样定义一个函数模板呢?
typedef boost::function<void()>;
//这里的两种定义都可以
typedef boost::function<void(void)>;
void 类型(空类型) 其实是C 中四种数据类型之一(基本数据类型,构造类型,指针类型,空类型)。空类型主要是用来修饰返回类型与函数参数的,注意空类型不能用来定义变量,eg :void i 是错误的。 可以认为空类型是一个抽象的基类,可以表示所有的类型。
参数的传入可以使用boost::bind,形成一个通用的函数模板。
2、Channel class
Channel class 的成员函数只能在IO线程被调用,因此更新数据成员不需要加锁,不存在跨线程访问的问题。
#ifndef _CHANNEL_H
#define _CHANNEL_H
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
class EventLoop;
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();}
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_; //POLL用到的索引
EventCallback readCallback_;
EventCallback writeCallback_;
EventCallback errorCallback_;
};
#endif
#include "Channel.h"
#include "EventLoop.h"
#include <poll.h>
const int Channel::kNoneEvent = 0;
const int Channel::kReadEvent = POLLIN | POLLPRI;
const int Channel::kWriteEvent = POLLOUT;
Channel::Channel(EventLoop* loop, int fdArg)
:loop_(loop),fd_(fdArg),events_(0),revents_(0),index_(-1)
{
}
void Channel::update()
{
loop_updateChannel(this);
}
void Channel::handleEvent()
{
if(revents_ & POLLNVAL){
std::cout<< "Channel::handle_event fd is not open" << endl;
if(revents_ & (POLLERR | POLLNVAL)){
if(errorCallback_) errorCallback_();}
if(revents_ & (POLLIN | POLLPRI | POLLRDHUP)){
if(readCallback_) readCallback_();}
if(revents_ & POLLOUT){
if(writeCallback_) writeCallback_();}
}
本文摘自陈硕老师的linux多线程服务端编程