在 Trio 中,如何在不等待的情况下将数据写入套接字?
In Trio, how do you write data to a socket without waiting?
在 Trio 中,如果您想将一些数据写入 TCP 套接字,那么显而易见的选择是 send_all
:
my_stream = await trio.open_tcp_stream("localhost", 1234)
await my_stream.send_all(b"some data")
请注意,这两者都通过套接字发送数据 并且 等待数据被写入。但是如果你只是想把要发送的数据排队,而不是等待它写入(至少,你不想在同一个协程中等待)怎么办?
在 asyncio 中这很简单,因为这两部分是独立的函数:write()
and drain()
。例如:
writer.write(data)
await writer.drain()
当然,如果您只想写入数据而不是等待它,您可以直接调用 write()
而无需等待 drain()
。 Trio 中是否有等效的功能?我知道这种双功能设计是有争议的,因为 it makes it hard to properly apply backpressure,但在我的应用程序中我需要将它们分开。
现在我已经通过为每个连接创建一个专用的 writer 协程并有一个内存通道将数据发送到该协程来解决这个问题,但是与在调用一个或两个函数之间进行选择相比,这是相当多的错误,而且看起来有点浪费(大概是引擎盖下还有一个发送缓冲区,而我的内存通道就像那个缓冲区之上的缓冲区)。
我在 Trio 聊天中发布了这个并且 Nathaniel J. Smith, the creator of Trio, replied with this:
Trio doesn't maintain a buffer "under the hood", no. There's just the kernel's send buffer, but the kernel will apply backpressure whether you want it to or not, so that doesn't help you.
Using a background writer task + an unbounded memory channel is basically what asyncio does for you implicitly.
The other option, if you're putting together a message in multiple pieces and then want to send it when you're done would be to append them into a bytearray and then call send_all once at the end, at the same place where you'd call drain in asyncio
(but obviously that only works if you're calling drain after every logical message; if you're just calling write and letting asyncio drain it in the background then that doesn't help)
所以这个问题是基于一个误解:我想写入Trio的隐藏发送缓冲区,但不存在这样的东西!使用一个单独的协程等待流并调用 send_all()
比我想象的更有意义。
我最终使用了两种想法的混合体(使用带有内存通道的单独协程与使用 bytearray):将数据保存到 bytearray,然后使用 条件变量 ParkingLot
向另一个协程发出信号,表明它已准备好写入。这让我可以合并写入,还可以手动检查缓冲区是否变得太大。
在 Trio 中,如果您想将一些数据写入 TCP 套接字,那么显而易见的选择是 send_all
:
my_stream = await trio.open_tcp_stream("localhost", 1234)
await my_stream.send_all(b"some data")
请注意,这两者都通过套接字发送数据 并且 等待数据被写入。但是如果你只是想把要发送的数据排队,而不是等待它写入(至少,你不想在同一个协程中等待)怎么办?
在 asyncio 中这很简单,因为这两部分是独立的函数:write()
and drain()
。例如:
writer.write(data)
await writer.drain()
当然,如果您只想写入数据而不是等待它,您可以直接调用 write()
而无需等待 drain()
。 Trio 中是否有等效的功能?我知道这种双功能设计是有争议的,因为 it makes it hard to properly apply backpressure,但在我的应用程序中我需要将它们分开。
现在我已经通过为每个连接创建一个专用的 writer 协程并有一个内存通道将数据发送到该协程来解决这个问题,但是与在调用一个或两个函数之间进行选择相比,这是相当多的错误,而且看起来有点浪费(大概是引擎盖下还有一个发送缓冲区,而我的内存通道就像那个缓冲区之上的缓冲区)。
我在 Trio 聊天中发布了这个并且 Nathaniel J. Smith, the creator of Trio, replied with this:
Trio doesn't maintain a buffer "under the hood", no. There's just the kernel's send buffer, but the kernel will apply backpressure whether you want it to or not, so that doesn't help you.
Using a background writer task + an unbounded memory channel is basically what asyncio does for you implicitly.
The other option, if you're putting together a message in multiple pieces and then want to send it when you're done would be to append them into a bytearray and then call send_all once at the end, at the same place where you'd call drain in asyncio
(but obviously that only works if you're calling drain after every logical message; if you're just calling write and letting asyncio drain it in the background then that doesn't help)
所以这个问题是基于一个误解:我想写入Trio的隐藏发送缓冲区,但不存在这样的东西!使用一个单独的协程等待流并调用 send_all()
比我想象的更有意义。
我最终使用了两种想法的混合体(使用带有内存通道的单独协程与使用 bytearray):将数据保存到 bytearray,然后使用 条件变量 ParkingLot
向另一个协程发出信号,表明它已准备好写入。这让我可以合并写入,还可以手动检查缓冲区是否变得太大。