在Trio中,如何不等待就将数据写入套接字?

问题描述

在Trio中,如果要将数据写入TCP套接字,那么显而易见的选择是send_all

my_stream = await trio.open_tcp_stream("localhost",1234)
await my_stream.send_all(b"some data")

请注意,这两者都通过套接发送该数据,并且等待写入。但是,如果您只是想将要发送的数据排队,而又不等待数据被写入(至少,您不想在同一个协程中等待)怎么办?

在异步中,这很简单,因为两个部分是独立的功能write()drain()。例如:

writer.write(data)
await writer.drain()

因此,当然,如果您只想写入数据而不等待数据,则可以调用write()而不用等待drain()。 Trio中有等效的功能吗?我知道这两种功能的设计存在争议,因为it makes it hard to properly apply backpressure,但是在我的应用程序中,我需要将它们分开。

就目前而言,我通过为每个连接创建专用的写入程序协程并具有一个将数据发送到该协程的内存通道来解决该问题,但是与在调用一个或两个函数之间进行选择相比,这相当麻烦。似乎有点浪费(大概在引擎盖下还有一个发送缓冲区,而我的内存通道就像在该缓冲区顶部的缓冲区一样。)

解决方法

我在Trio聊天室和Nathaniel J. Smith的创建者replied with this上发布了此信息:

Trio不会在“后台”维护缓冲区,不。仅有内核的发送缓冲区,但是无论您是否需要内核,内核都会施加反压,因此这无济于事。

基本上,asyncio隐式地使用后台编写器任务+无限制的内存通道。

另一种选择,如果您将一条消息放在一起,然后想在完成后发送,则将它们附加到一个字节数组中,然后最后一次在同一位置调用send_all您会在asyncio中调用消耗

的地方

(但是很明显,这只有在您在每条逻辑消息后都调用drain时才有效;如果您只是调用write并让asyncio在后台将其消耗掉,那将无济于事)

所以这个问题是基于一个误解:我想写到Trio的隐藏发送缓冲区中,但是不存在这样的东西!使用一个等待流并调用send_all()的单独协程比我想象的要有意义。

我最终使用了两种想法的混合体(使用带有内存通道的独立协程与使用字节数组):将数据保存到字节数组,然后使用条件变量向其他协程发信号,表明已准备就绪。这使我可以合并写入,还可以手动检查缓冲区是否过大。