Proactor VS Reactor

问题描述 投票:0回答:2

Proactor 和 Reactor 之间的真正区别是什么?

  • Proactor 调度一个函数,然后轮询返回的 future 以获得就绪结果。
  • Reactor 将函数分派到事件循环中,并在事件循环返回时轮询准备好的结果。
c++ design-patterns concurrency reactive-programming reactor-pattern
2个回答
5
投票

我认为对于 Reactor 和 Proactor 设计模式之间的具体差异有多种定义。我目前的想法是,最好关注重复阅读的语义。

  • Reactor:用户向reactor注册通道,提供回调。此后,每当有新数据可供读取时,反应器就会通过调用此回调来通知用户。然后,用户可以在回调中从通道读取一些数据。因此,提供单个回调(在注册时)可能会导致多次调用此回调。

  • 前摄器:每当用户想要从通道读取一些数据时,他们都会向前摄器发出异步请求,提供回调和指向某些缓冲区空间的指针/大小。此后,当通道中可以读取新数据时,前摄器会将此数据复制到用户提供的缓冲区空间中并调用回调。这样就完成了异步请求。用户必须确保缓冲区空间保持有效/在范围内/可写,直到异步请求完成。如果用户想要读取更多数据,则必须显式发出另一个异步请求。因此(在请求时)提供单个回调总是会导致回调的一次调用。在这种模式中,通常会编写回调,以便在回调结束时发出另一个异步请求(这在 ASIO 文档中称为“异步操作链”;ASIO 遵循 Proactor 模式)。

Linux API epoll (=reactor) 和 io_uring (=proactor) 可以体现这种差异。使用 epoll,您可以通过

::epoll_ctl(fd, EPOLL_CTL_ADD, ...)
将文件描述符添加到内核 epoll 对象一次,然后该文件描述符可以在
::epoll_wait(...)
的结果中多次出现。使用 io_uring,用户每提交一个提交队列条目(sqe)都会收到一个完成队列条目(cqe)(但我对 io_uring 不太熟悉;我在这里可能是错的)。

因此,当存在大量短读取时,前摄器模式会导致更多开销。我怀疑这就是io_uring有时会比epoll慢的原因:

不过,可以给出不同的定义。例如,定义可以围绕是否(a.1)用户提供指向某个缓冲区空间的指针/大小,并保证在异步请求完成之前该缓冲区空间有效,或者(a.2)用户是否主动读取缓冲区空间。数据进入回调内的缓冲区。人们还可以将定义集中在是否使用(b.1)回调(用户可以对事件做出反应)或是否(b.2)用户主动检查某些通道可读的数据结构。不过,这些方面与上述方面是正交的,我发现将定义集中在(c.1)用户回调通常是否必须主动发出新的异步请求(c.2)是最有帮助的.

  • ASIO 是 (a.1) (b.1) (c.1)
  • epoll 是 (a.2) (b.2) (c.2)
  • io_uring 是 (a.1) (b.2) (c.1) (我认为,但我可能是错的)

4
投票

Proactor 或 Reactor 主要是关于我们如何主动或被动地处理 IO 读/写:

  • Proactor会主动处理IO读/写,只有读/写完成时才会通知用户,这样用户就可以专注于业务逻辑
  • Reactor仅反应性地处理IO读/写,当有任何内容准备好读/写时,它会通知用户,以便用户可以读/写数据,并处理业务逻辑
© www.soinside.com 2019 - 2024. All rights reserved.