使用epoll_wait时如何正确读取数据

cha*_*med 4 c++ linux multithreading epoll recv

我正在尝试将使用IOCP的现有Windows C ++代码移植到Linux。决定使用它epoll_wait来实现高并发性后,我已经面临一个理论问题,即我们何时尝试处理接收到的数据。

想象一下,有两个线程调用epoll_wait,并且收到了两个consequetives消息,以便Linux取消阻塞第一个线程,并很快阻塞第二个线程。

范例:

Thread 1 blocks on epoll_wait
Thread 2 blocks on epoll_wait
Client sends a chunk of data 1
Thread 1 deblocks from epoll_wait, performs recv and tries to process data
Client sends a chunk of data 2
Thread 2 deblocks, performs recv and tries to process data.
Run Code Online (Sandbox Code Playgroud)

这种情况可以想象吗?即会发生吗?

有没有一种方法可以避免这种情况,从而避免在recv /处理代码中实现同步?

bdo*_*lan 5

如果您有多个线程从同一组epoll句柄中读取,建议您使用,将epoll句柄置于一次触发的级别触发模式EPOLLONESHOT。这样可以确保在一个线程观察到触发的句柄之后,除非您使用epoll_ctl重新武装该句柄,否则其他线程将不会观察到它。

如果您需要独立处理读写路径,则可能需要完全拆分读写线程池。具有一个用于读事件的epoll句柄,一个用于写事件的epoll句柄,并将线程专门分配给一个或另一个。此外,为读取和写入路径设置单独的锁。当然,就修改任何套接字状态而言,您必须小心读写线程之间的交互。

如果您确实采用这种拆分方法,则需要对如何处理套接字关闭进行思考。很可能您会希望在共享数据锁下为读取和写入路径设置一个附加的共享数据锁和“确认关闭”标志。然后,读取和写入线程可以竞相确认,最后一个确认线程将清理共享数据结构。也就是说,像这样:

void OnSocketClosed(shareddatastructure *pShared, int writer)
{
  epoll_ctl(myepollhandle, EPOLL_CTL_DEL, pShared->fd, NULL);
  LOCK(pShared->common_lock);
  if (writer)
    pShared->close_ack_w = true;
  else
    pShared->close_ack_r = true;

  bool acked = pShared->close_ack_w && pShared->close_ack_r;
  UNLOCK(pShared->common_lock);

  if (acked)
    free(pShared);
}
Run Code Online (Sandbox Code Playgroud)