zoukankan      html  css  js  c++  java
  • eventfd实现线程事件通知机制

     通过eventfd实现的事件通知机制


    eventfd的使用

    eventfd系统函数

    eventfd  - 事件通知文件描述符

    #include <sys/eventfd.h>
    int eventfd(unsigned int initval ,int flags );

    创建一个能被用户应用程序用于时间等待唤醒机制的eventfd对象.
    initval :
    eventfd()创建一个可用作事件的“eventfd对象”用户空间应用程序和内核等待/通知机制通知用户空间应用程序的事件。该对象包含一个由内核维护的无符号64位整型(uint64_t)计数器。此计数器的初始值通过initval指定。一般设0.

    flags :
    以下标志中按位OR运算以更改eventfd()的行为,(文件中常用的这两个flags肯定都懂意思吧,就不翻译了,第三个信号量的不管它.):

       EFD_CLOEXEC (since Linux 2.6.27)
              Set the close-on-exec (FD_CLOEXEC) flag on the new file
              descriptor.  See the description of the O_CLOEXEC flag in
              open(2) for reasons why this may be useful.
    
       EFD_NONBLOCK (since Linux 2.6.27)
              Set the O_NONBLOCK file status flag on the new open file
              description.  Using this flag saves extra calls to fcntl(2) to
              achieve the same result.
    
       EFD_SEMAPHORE (since Linux 2.6.30)
              Provide semaphore-like semantics for reads from the new file
              descriptor.  See below.

    read(2)

    成功读取返回一个8byte的整数。read(2)如果提供的缓冲区的大小小于8个字节返回错误EINVAL

    write (2)

    将缓冲区写入的8字节整形值加到内核计数器上。可以写入的最大值
    是计数器中是最大的无符号64位值减1(即0xfffffffffffffffe)。

    返回值:

    On success, eventfd() returns a new eventfd file descriptor. On error, -1 is returned and errno is set to indicate the error.

    使用示例

    #include <iostream>
    #include <assert.h>
    #include <poll.h>
    #include <signal.h>
    #include <sys/eventfd.h>
    #include <unistd.h>
    #include <string.h>
    #include <thread>
    
    static int s_efd = 0;
    
    int createEventfd()
    {
      int evtfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
    
      std::cout << "createEventfd() fd : " << evtfd << std::endl;
    
      if (evtfd < 0)
      {
        std::cout << "Failed in eventfd
    ";
        abort();
      }
    
      return evtfd;
    }
    
    void testThread()
    {
      int timeout = 0;
      while(timeout < 3) {
        sleep(1);
        timeout++;
      }
    
      uint64_t one = 1;
      ssize_t n = write(s_efd, &one, sizeof one);
      if(n != sizeof one)
      {
        std::cout << " writes " << n << " bytes instead of 8
    ";
      }
    }
    
    int main()
    {
      s_efd = createEventfd();
    
      fd_set rdset;
      FD_ZERO(&rdset);
      FD_SET(s_efd, &rdset);
    
      struct timeval timeout;
      timeout.tv_sec = 1;
      timeout.tv_usec = 0;
    
      std::thread t(testThread);
    
      while(1)
      {
        if(select(s_efd + 1, &rdset, NULL, NULL, &timeout) == 0)
        {
          std::cout << "timeout
    ";
          timeout.tv_sec = 1;
          timeout.tv_usec = 0;
          FD_SET(s_efd, &rdset);
            continue;
        }
    
        uint64_t one = 0;
    
        ssize_t n = read(s_efd, &one, sizeof one);
        if(n != sizeof one)
        {
          std::cout << " read " << n << " bytes instead of 8
    ";
        }
    
        std::cout << " wakeup !
    ";
    
        break;
      }
    
      t.join();
      close(s_efd);
    
      return 0;
    }
    ./test.out
    createEventfd() fd : 3
    timeout
    timeout
    timeout
     wakeup !

    eventfd 单纯的使用文件描述符实现的线程间的通知机制,可以很好的融入select、poll、epoll的I/O复用机制中.

    EventLoop对eventfd的封装

    所增加的接口及成员:

        typedef std::function<void()> Functor;
        void runInLoop(const Functor& cb);
        void wakeup(); //是写m_wakeupFd 通知poll 处理读事件.
        void queueInLoop(const Functor& cb);
    private:
        //used to waked up
        void handleRead();
        void doPendingFunctors();
        
        int m_wakeupFd;
        std::unique_ptr<Channel> p_wakeupChannel;
        mutable MutexLock m_mutex;
        bool m_callingPendingFunctors; /* atomic */
        std::vector<Functor> m_pendingFunctors; // @GuardedBy mutex_

    工作时序

    (runInLoop() -> quueInLoop())/queueInLoop() -> wakeup() -> poll() -> handleRead() -> doPendingFunctors()

    runInLoop()

    如果用户在当前IO线程调用这个函数, 回调会同步进行; 如果用户在其他线程调用runInLoop(),cb会被加入队列, IO线程会被唤醒来调用这个Functor.

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

    queueInLoop()

    会将回调添加到容器,同时通过wakeup()唤醒poll()调用容器内的回调.

    void EventLoop::queueInLoop(const Functor& cb)
    {
      LOG_TRACE << "EventLoop::queueInLoop()";
      {
        MutexLockGuard lock(m_mutex);
        m_pendingFunctors.push_back(std::move(cb));
      }
    
      if(!isInloopThread())
      {
        wakeup();
      }
    }

    内部实现,

    wakeup()

    写已注册到poll的eventfd 通知poll 处理读事件.

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

    handleRead()

    poll回调读事件,处理eventfd.

    void EventLoop::handleRead() //handle wakeup Fd
    {
      LOG_TRACE << "EventLoop::handleRead() handle wakeup Fd";
      uint64_t one = 1;
      ssize_t n = sockets::read(m_wakeupFd, &one, sizeof one);
      if(n != sizeof one)
      {
        LOG_ERROR << "EventLoop::handleRead() reads " << n << "bytes instead of 8";
      }
      doPendingFunctors();
    }

    doPendingFunctors()

    处理挂起的事件.

    void EventLoop::doPendingFunctors()
    {
      LOG_TRACE << "EventLoop::doPendingFunctors()";
      std::vector<Functor> functors;
      m_callingPendingFunctors = true;
    
      {
        MutexLockGuard lock(m_mutex);
        functors.swap(m_pendingFunctors);
      }
    
      for(size_t i = 0; i < functors.size(); ++i)
      {
        functors[i]();
      }
    
      m_callingPendingFunctors = false;
    
    }
     
  • 相关阅读:
    ASP.NET 分页数据源:: PagedDataSource //可分页数据源
    strtok
    FloydWarshall算法详解(转)
    Tom Clancy's Splinter Cell: Double Agent
    暴雪COO确认:星际争霸2.0要来了
    wxWidgets 2.8.0 released
    如饥似渴
    大乘法器遇见小乘法器
    GLEW 1.3.5 adds OpenGL 2.1 and NVIDIA G80 extensions
    DevIL真是好用得想哭
  • 原文地址:https://www.cnblogs.com/developing/p/10887779.html
Copyright © 2011-2022 走看看