我有一个运行非阻塞事件循环的线程,其他线程可以将字符串放在缓冲区上,以便事件循环写入套接字。
我想在缓冲区中累积字符串,这样就可以通过一次调用send
来发送多个小字符串。
从概念上讲,缓冲区需要做3件事。
我考虑了一些事情。
StringIO
非常适合在最后编写字符串,但在写入套接字后无法释放数据。collections.deque
个字节。内存效率很低。array.array
很容易追加字符串。复制切片以读取/转储数据。我目前的代码看起来像这样,但我对复制和锁定并不十分满意。
from array import array
from threading import Condition
class SendBuffer(object):
def __init__(self, max_size):
self.mark = 0
self.buf = array('c')
self.max_size = max_size
self.full = Condition()
def __len__(self):
with self.full:
return len(self.buf) - self.mark
def write(self, data):
with self.full:
while len(self) >= self.max_size:
# wait until data is written
self.full.wait()
self.buf.fromstring(data)
def _peek(self):
return buffer(self.buf, self.mark)
def _written(self, n):
self.mark += n
self.full.notify_all()
if self.mark >= len(self.buf):
self.mark = 0
self.buf = array('c')
elif self.mark >= self.max_size:
self.buf = self.buf[self.mark:]
self.mark = 0
def to_sock(self, sock):
with self.full:
data = self._peek()
if data:
n = sock.send(data)
self._written(n)
答案 0 :(得分:1)
您的问题是您的缓冲区(如StringIO)只能附加到其中。当您完成处理时,请执行以下操作,而不是追加到最后并从前面删除:
让我们考虑一些案例:
读取器超过了写入器:每次写入后立即读取相同大小的读取,并且缓冲区交换位置。每次写入都会立即作为单个数据包发出。
读取器和写入器完全同步,或者与一些抖动足够接近:多个小写入累积到写入缓冲区直到读取器完成,然后它们以与网络一样大的块发送出去将采取。
作者超越了读者。当读取器忙于处理读缓冲区时,写缓冲区将填满。读者仍然会发送网络所需的大块,但是你需要以某种方式限制编写器(通常通过设置最大缓冲区大小)并调整它们以避免占用无限量的内存。
请记住,缓冲区只是防止抖动导致停顿的一种方法。他们无法帮助抵御不匹配的生产者/消费者速度。实际上,您的缓冲区将持续满或不断为空。
(*)清除一个StringIO对象显然不是微不足道的,谷歌一点点。您可能想要创建一个新对象而不是清除,但如果您有许多上下文切换,这可能会导致大量垃圾需要GC。相反,您也可以考虑使用数组和index
变量的组合构建自己的可清除缓冲区,在这种情况下,清除将降至index = 0
。