linux多线程服务器编程 muduo库学习笔记

本文介绍使用C++库muduo进行Linux服务器端编程的方法。重点讲解了EventLoop、Channel和Poller三个核心组件的功能及交互过程,并提供了简单的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

知识点: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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值