29muduo_net库源码分析(五)

本文介绍muduo库中的线程间通信机制,包括使用eventfd进行线程唤醒的方法,以及如何通过EventLoop类实现任务调度,确保线程间的正确同步。

1.进程(线程)wait/notify

(1)pipe
(2)socketpair
(3)eventfd,eventfd是一个比 pipe更高效的线程间事件通知机制,一方面它比 pipe 少用一个file descripor,节省了资源;另一方面,eventfd的缓冲区管理也简单得多,全部“buffer”只有定长8 bytes,不像 pipe 那样可能有不定长的真正buffer。
int createEventfd()
{
  int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
  if (evtfd < 0)
  {
    LOG_SYSERR << "Failed in eventfd";
    abort();
  }
  return evtfd;
}

}

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";
  }
}

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


2.流程图



(1)调用queueInLoop的线程不是当前IO线程需要唤醒

(2)或者调用queueInLoop的线程是当前IO线程,并且此时正在调用pendingfunctor,需要唤醒(也就是当前IO线程执行doPendingFunctors时候,调用了queueInLoop)

(3)只有IO线程的事件回调中调用queueInLoop才不需要唤醒


3.代码

1.EventLoop::runInLoop

// 在I/O线程中执行某个回调函数,该函数可以跨线程调用
void EventLoop::runInLoop(const Functor& cb)
{
  if (isInLoopThread())
  {
    // 如果是当前IO线程调用runInLoop,则同步调用cb
    cb();
  }
  else
  {
    // 如果是其它线程调用runInLoop,则异步地将cb添加到队列
    queueInLoop(cb);
  }
}


2.EventLoop::queueInLoop

void EventLoop::queueInLoop(const Functor& cb)
{
  {
  MutexLockGuard lock(mutex_);
  pendingFunctors_.push_back(cb);
  }
  // 调用queueInLoop的线程不是当前IO线程需要唤醒
  // 或者调用queueInLoop的线程是当前IO线程,并且此时正在调用pending functor,需要唤醒
  // 只有当前IO线程的事件回调中调用queueInLoop才不需要唤醒
  if (!isInLoopThread() || callingPendingFunctors_)
  {
    wakeup();
  }
}

3.EventLoop:loop

 while (!quit_)
  {
    activeChannels_.clear();
    pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);
    if (Logger::logLevel() <= Logger::TRACE)
    {
      printActiveChannels();
    }
    eventHandling_ = true;
    for (ChannelList::iterator it = activeChannels_.begin();
        it != activeChannels_.end(); ++it)
    {
      currentActiveChannel_ = *it;
      currentActiveChannel_->handleEvent(pollReturnTime_);
    }
    currentActiveChannel_ = NULL;
    eventHandling_ = false;
    doPendingFunctors();
  }

4.EventLoop::doPendingFunctors

void EventLoop::doPendingFunctors()
{
  std::vector<Functor> functors;
  callingPendingFunctors_ = true;

  {
  MutexLockGuard lock(mutex_);
  functors.swap(pendingFunctors_);
  }

  for (size_t i = 0; i < functors.size(); ++i)
  {
    functors[i]();
  }
  callingPendingFunctors_ = false;
}

(1)不是简单地在临界区内依次调用Functor,而是把回调列表swapfunctors中,这样一方面减小了临界区的长度(意味着不会阻塞其它线程的queueInLoop()),另一方面,也避免了死锁(因为Functor可能再次调用queueInLoop()

(2)由于doPendingFunctors()调用的Functor可能再次调用queueInLoop(cb),这时,queueInLoop()就必须wakeup(),否则新增的cb可能就不能及时调用了

(2)muduo没有反复执行doPendingFunctors()直到pendingFunctors为空,这是有意的,否则IO线程可能陷入死循环,无法处理IO事件。


4.测试

#include <muduo/net/EventLoop.h>
//#include <muduo/net/EventLoopThread.h>
//#include <muduo/base/Thread.h>

#include <stdio.h>

using namespace muduo;
using namespace muduo::net;

EventLoop* g_loop;
int g_flag = 0;

void run4()
{
  printf("run4(): pid = %d, flag = %d\n", getpid(), g_flag);
  g_loop->quit();
}

void run3()
{
  printf("run3(): pid = %d, flag = %d\n", getpid(), g_flag);
  g_loop->runAfter(3, run4);
  g_flag = 3;
}

void run2()
{
  printf("run2(): pid = %d, flag = %d\n", getpid(), g_flag);
  g_loop->queueInLoop(run3);
}

void run1()
{
  g_flag = 1;
  printf("run1(): pid = %d, flag = %d\n", getpid(), g_flag);
  g_loop->runInLoop(run2);
  g_flag = 2;
}

int main()
{
  printf("main(): pid = %d, flag = %d\n", getpid(), g_flag);

  EventLoop loop;
  g_loop = &loop;

  loop.runAfter(2, run1);
  loop.loop();
  printf("main(): pid = %d, flag = %d\n", getpid(), g_flag);
}


### muduo网络源码解读与分析 muduo网络是由陈硕(Chen Shuo)开发的一个高性能的C++网络,主要用于构建跨平台的网络服务应用。它基于Linux平台,充分利用了现代C++特性以及高效的系统调用机制(如`epoll`),提供了线程池、事件循环、TCP连接管理等功能。以下是关于muduo网络源码的解读和分析: #### 1. 设计理念 muduo的设计目标是提供一个简单、高效且易于扩展的网络编程框架。它的设计遵循了现代C++的最佳实践,例如RAII(Resource Acquisition Is Initialization)[^1],避免了资源泄漏问题,并通过智能指针管理对象生命周期。此外,muduo还强调了代码的可读性和可维护性。 #### 2. 核心组件 muduo的核心组件包括以下几个部分: - **EventLoop**:事件循环模块,负责监听和分发事件。 - **Channel**:封装了文件描述符(file descriptor)及其相关的事件。 - **Poller**:具体实现事件轮询功能,基于`epoll`或其他类似的机制。 - **TcpConnection**:表示一个TCP连接,包含读写缓冲区、状态机等。 - **TcpServer**:用于创建和管理多个TCP连接。 - **Buffer**:高效的数据缓冲区,支持零拷贝操作。 #### 3. 源码结构 muduo的源码结构清晰,按照功能模块进行了划分。以下是一些主要目录及其作用: - `base/`:包含通用的基础工具类,如`Logging`、`Thread`、`Timestamp`等。 - `net/`:核心网络的实现,包括`EventLoop`、`TcpServer`、`TcpConnection`等。 - `examples/`:一些示例程序,展示了如何使用muduo构建实际应用。 - `tests/`:单元测试代码,验证的功能是否正确。 #### 4. 关键技术点 - **Reactor模式**:muduo采用了经典的Reactor模式来处理I/O事件,使得单线程可以高效地管理大量连接[^3]。 - **非阻塞I/O**:通过`epoll`实现高效的非阻塞I/O操作,避免了传统阻塞模型的性能瓶颈。 - **智能指针**:广泛使用`std::shared_ptr`和`std::unique_ptr`来管理动态分配的对象,确保资源的安全释放。 - **线程安全**:在多线程环境下,muduo通过锁机制或无锁队列保证数据的一致性。 #### 5. 学习资源 对于希望深入理解muduo网络的开发者,以下资源可能会有所帮助: - **官方文档**:虽然muduo没有详细的官方文档,但其源码本身非常清晰,适合阅读和学习。 - **书籍推荐**:W. Richard Stevens的《UNIX网络编程》系列是学习网络编程的经典教材,可以帮助理解muduo的设计思想[^2]。 - **社区讨论**:GitHub上有很多关于muduo的讨论和案例分析,可以作为参考。 - **博客文章**:一些开发者撰写了关于muduo源码的详细解读文章,可以通过搜索引擎查找。 ```python # 示例代码:简单的TcpServer使用 from muduo.net import TcpServer, InetAddress, EventLoop def on_connection(conn): if conn.is_connected(): print("New connection") else: print("Connection closed") def on_message(conn, buf, timestamp): print(f"Received: {buf}") loop = EventLoop() server = TcpServer(loop, InetAddress(9981), "TestServer") server.set_connection_callback(on_connection) server.set_message_callback(on_message) server.start() loop.loop() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值