在订阅时发送环形缓冲区的全部内容,然后发送新数据

send the full contents of a ring buffer on subscription and then send new data

我是 boost::asio 的初学者。

我需要编写一个从管道读取数据并将数据放入环形缓冲区的模块(我对如何实现这部分没问题)。

模块的另一部分等待消费者打开一个新的 TCP 连接或 unix 域套接字,当连接建立时它发送完整的环形缓冲区内容,然后它会尽快发送任何新数据推入环形缓冲区。允许多个消费者,一个消费者可以随时打开一个新连接。

我想到的第一个天真的实现是为每个连接保留一个单独的 asio::streambuf 并在连接时将整个环形缓冲区推送到其中,然后是每个新数据,但这似乎是一个非常次优的方法在内存和 cpu 周期中执行此操作,因为必须为每个连接复制数据,可能多次,因为我不知道 boost::asio::send (或 linux tcp/ip stack) 复制数据。

因为我的想法是完全不使用多线程,所以我正在考虑使用某种形式的自定义 asio::streambuf 派生 class,它与环形缓冲区共享实际缓冲区,但保留不需要任何锁的读指针的单独状态。

这对我来说似乎是一个非常不寻常的需求,因为我找不到任何涉及类似主题的相关 documentation/question 并且 boost 文档对我来说似乎非常简短和稀缺(参见例如: http://www.boost.org/doc/libs/1_57_0/doc/html/boost_asio/reference/basic_streambuf.html).

如果有人能给我指出一些我可以作为实现我设计的起点的想法,或者如果 he/she 认为我的设计不好,无法实现,请给我指出一个替代设计,那就太好了and/or 可改进。

你应该做你想做的。

你绝对不需要 streambuf 来使用 Boost Asio:http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/buffer.html

如果问题是如何避免生产者 "wait" 直到所有消费者(读取:连接)完成数据传输,您始终可以使用 旧技巧 的交替输出缓冲器。

许多环形缓冲区实现允许一次直接拼接完整的元素序列(例如 boost lockfree spsc_queue cache memory access)。您可以利用这样的操作来发挥自己的优势。

也相关:

  • TCP Zero copy using boost

看来,性能是这里的一个话题。 无论是使用 boost::asio 还是某种手工编织的解决方案,性能(吞吐量)可能已经被消耗殆尽(如 OP 的评论部分所述),单个字节正在被交易(阅读来自管道)。
在消费者连接的初始 "burst phase" 之后,单个字节从管道滴流到连接的消费者套接字,每个字节都有 read() 和 write() 操作(如果应用程序不经常轮询,则为几个字节)。
考虑到这一点(系统调用 read() 和 write() 的价格是为少量数据支付的事实),我敢说关于多队列或单队列等的任何事情都已经在那个基本 "design flaw"。我把 "design flaw" 放在引号中,因为总是不可避免地必须处理这种情况。

所以,如果无论如何都无法优化吞吐量,我会推荐可以想到的最简单直接的解决方案。

OP 中的 "no threads" 语句暗示了接受套接字、消费者数据套接字和管道的非阻塞文件描述符。这会是另一个 100% CPU/core 吃投票的应用程序吗?如果这不是某种特殊的 ops 超优化问题,我宁愿不建议使用非阻塞文件描述符。另外,我不会担心零拷贝与否。

使用线程的一种简单方法是让消费者套接字处于非阻塞状态,而管道处于阻塞模式。读取管道的线程然后将数据泵入队列并调用为所有当前连接的消费者提供服务的函数。当新的客户端连接挂起时,侦听套接字(调用 accept() 的套接字)处于信号状态。使用 kqueue (bsd) 或 epoll(linux 等)或 WaitForMultipleObjects(windows)等机制,管道 reader 线程也可以对这种情况做出反应。

在无事可做的时代,您的应用程序 sleeping/blocking 并且对我们的环境友好 :)