0

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

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

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

在 asyncio 中这很简单,因为这两个部分是独立的函数:write()drain(). 例如:

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

因此,当然,如果您只想写入数据而不是等待它,您可以直接调用write()而不等待drain()。Trio 中是否有等效的功能?我知道这种双功能设计是有争议的,因为它很难正确应用背压,但在我的应用程序中,我需要将它们分开。

现在我已经通过为每个连接创建一个专用的编写器协程并有一个内存通道来将数据发送到该协程来解决它,但是与在调用一个或两个函数之间进行选择相比,它是相当多的faff,而且它似乎是一个有点浪费(大概引擎盖下还有一个发送缓冲区,我的内存通道就像那个缓冲区顶部的缓冲区)。

4

1 回答 1

1

我在 Trio 聊天中发布了这个,Trio的创建者Nathaniel J. Smith回复了这个

Trio 不会在“引擎盖下”维护缓冲区,不。只有内核的发送缓冲区,但无论你是否愿意,内核都会应用背压,所以这对你没有帮助。

使用后台编写器任务 + 无限内存通道基本上是 asyncio 隐式为您所做的。

另一种选择是,如果您将一条消息分成多个部分,然后想在完成后发送它,则将它们附加到一个字节数组中,然后在最后调用一次 send_all,在同一个地方d 在 asyncio 中调用 drain

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

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

我最终使用了这两种想法的混合体(使用带有内存通道的单独协程与使用字节数组):将数据保存到字节数组,然后使用条件变量 ParkingLot向另一个协程发出信号,表明它已准备好写入。这让我可以合并写入,并手动检查缓冲区是否变得太大。

于 2020-09-08T19:41:15.583 回答