bal*_*lki 6 python streamwriter python-3.x python-asyncio
来自doc:https: //docs.python.org/3/library/asyncio-stream.html#asyncio.StreamWriter.write
写(数据)
Run Code Online (Sandbox Code Playgroud)Write data to the stream. This method is not subject to flow control. Calls to write() should be followed by drain().coroutine drain()
Run Code Online (Sandbox Code Playgroud)Wait until it is appropriate to resume writing to the stream. Example: writer.write(data) await writer.drain()
据我所知,
drain每次打电话都要write打电话.write将阻止循环线程那为什么写不是自动调用它的协同程序?为什么一个人打电话write而不必排水?我可以想到两个案例
write和close立即第一个是特例,我想我们可以有一个不同的api.缓冲应该在写入功能内部处理,应用程序不应该在意.
让我以不同的方式提出问题.这样做有什么缺点?python3.8版本有效吗?
async def awrite(writer, data):
writer.write(data)
await writer.drain()
Run Code Online (Sandbox Code Playgroud)
注意:draindoc明确说明如下:
当没有什么可以等待时,drain()立即返回.
再次阅读答案和链接,我认为这些功能是这样的.注意:检查已接受的答案以获得更准确的版本
def write(data):
remaining = socket.try_write(data)
if remaining:
_pendingbuffer.append(remaining) # Buffer will keep growing if other side is slow and we have a lot of data
async def drain():
if len(_pendingbuffer) < BUF_LIMIT:
return
await wait_until_other_side_is_up_to_speed()
assert len(_pendingbuffer) < BUF_LIMIT
async def awrite(writer, data):
writer.write(data)
await writer.drain()
Run Code Online (Sandbox Code Playgroud)
那么何时使用:
writeawriteawrite.如果文件很大,loop.sendfile如果可用的话会更好.据我所知,(1)每次调用时都需要调用drain.(2)如果不是,我猜,写将阻止循环线程
两者都不正确,但混乱是可以理解的.write()工作方式如下:
调用write()只是将数据存储到缓冲区,将其留在事件循环中以便稍后将其写出,而无需程序的进一步干预.就应用而言,数据在后台写入的速度与对方能够接收的速度一样快.换句话说,每一个write()将调度其数据到使用尽可能多操作系统级写入,因为它需要,与当对应的文件描述符是实际上可写发布的那些写入被传送.所有这一切都是自动发生的,即使没有等待drain().
write()不是协程,它绝对不会阻止事件循环.
第二个属性听起来方便,但它实际上是一个重大缺陷的write().写作是从接受数据分离,因此,如果你写的数据比你的同行更快的可以读取它,内部缓冲区将继续增长,你就会有一个内存泄漏在您的手中.drain()一旦缓冲区变得太大,等待暂停协同程序.每次写入drain()后都不需要等待,但是您需要偶尔等待它,通常是在循环迭代之间.例如:
while True:
response = await peer1.readline()
peer2.write(b'<response>')
peer2.write(response)
peer2.write(b'</response>')
await peer2.drain()
Run Code Online (Sandbox Code Playgroud)
drain()如果待处理的未写入数据量很小,则立即返回.如果数据超过高阈值,drain()将暂停调用协程,直到待处理的未写入数据量低于低阈值.暂停将导致协同程序停止读取peer1,这将导致对等方降低向其发送数据的速率.这种反馈被称为背压.
正如asyncio开发人员所报告的那样,Python 3.8将支持write删除对显式的需求drain().(同时补充了支持.)
缓冲应该在写入功能内部处理,应用程序不应该在意.
这几乎是write()现在的工作方式 - 它确实处理缓冲,它让应用程序不关心,无论好坏.另请参阅此答案以获取更多信息.
再次阅读答案和链接,我认为这些功能是这样的.
write()仍然比那更聪明.它不会尝试只写一次,它实际上会安排数据继续写入,直到没有数据写入.即使你永远等待也会发生这种情况drain()- 应用程序必须做的唯一事情是让事件循环运行足够长的时间来写出所有内容.
一个更正确的伪代码write,并drain可能是这样的:
class ToyWriter:
def __init__(self):
self._buf = bytearray()
self._empty = asyncio.Event(True)
def write(self, data):
self._buf.extend(data)
loop.add_writer(self._fd, self._do_write)
self._empty.clear()
def _do_write(self):
# Automatically invoked by the event loop when the
# file descriptor is writable, regardless of whether
# anyone calls drain()
while self._buf:
try:
nwritten = os.write(self._fd, self._buf)
except OSError as e:
if e.errno == errno.EWOULDBLOCK:
return # continue once we're writable again
raise
self._buf = self._buf[nwritten:]
self._empty.set()
loop.remove_writer(self._fd, self._do_write)
async def drain(self):
if len(self._buf) > 64*1024:
await self._empty.wait()
Run Code Online (Sandbox Code Playgroud)
实际实现更复杂,因为:
os.write;drain()不是真的要等到缓冲区为空,而是直到它达到低水印 ;EWOULDBLOCK提出之外的异常_do_write被存储和重新提出drain().最后一点是调用的另一个好理由drain(),实际上注意到对等体通过写入失败而消失了.
| 归档时间: |
|
| 查看次数: |
785 次 |
| 最近记录: |