来自 doc :
write(data)
Write data to the stream. This method is not subject to flow control. Calls to write() should be followed by drain().coroutine drain()
Wait until it is appropriate to resume writing to the stream. Example: writer.write(data) await writer.drain()
据我了解,
write时都需要调用drain。write 会阻塞循环线程那write为什么不是自动调用的协程呢?为什么一个调用 write 而不必耗尽?我可以想到两种情况
编写并关闭第一个是特例,我想我们可以有不同的 API。缓冲应该在写函数内部处理,应用程序不应该关心。
让我以不同的方式提出这个问题。这样做的缺点是什么? python3.8版本有效吗?
async def awrite(writer, data):
writer.write(data)
await writer.drain()
注意:drain 文档明确说明如下:
When there is nothing to wait for, the
drain()returns immediately.
再次阅读答案和链接,我认为这些功能是这样工作的。 注意:检查已接受的答案以获得更准确的版本。
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()
那么什么时候使用什么:
writeawriteawrite。如果文件很大,loop.sendfile 如果可用的话会更好。最佳答案
From what I understand, (1) You need to call drain every time write is called. (2) If not I guess, write will block the loop thread
两者都不正确,但混淆是可以理解的。 write() 的工作方式如下:
对 write() 的调用只是将数据存储到缓冲区,将其留给事件循环以在稍后实际写出,而无需程序进一步干预.就应用程序而言,数据在后台写入的速度与另一方接收数据的速度一样快。换句话说,每个 write() 都会安排它的数据使用尽可能多的操作系统级写入来传输,这些写入在相应的文件描述符实际可写时发出。所有这一切都会自动发生,甚至无需等待 drain()。
write() 不是协程,它绝对从不阻塞事件循环。
第二个属性听起来很方便——你可以在任何需要的地方调用write(),甚至是从一个不是async def的函数调用——但它实际上是一个主要的write() 的>缺陷。流 API 公开的写入与接受数据的操作系统完全分离,因此如果您写入数据的速度快于网络对等体读取数据的速度,内部缓冲区将不断增长,您将拥有 memory leak。在你的手上。 drain() 修复了这个问题:如果写入缓冲区变得太大,等待它会暂停协程,并在 os.write() 执行后再次恢复协程后台成功,缓冲区缩小。
您不需要在每次 写入之后等待drain(),但您确实需要偶尔等待它,通常是在write() 被调用。例如:
while True:
response = await peer1.readline()
peer2.write(b'<response>')
peer2.write(response)
peer2.write(b'</response>')
await peer2.drain()
drain() 如果待处理的未写入数据量很小,则立即返回。如果数据超过高阈值,drain() 将暂停调用协程,直到待处理的未写入数据量降至低阈值以下。暂停将导致协程停止从 peer1 读取数据,这又会导致对等方减慢它向我们发送数据的速率。这种反馈称为背压。
Buffering should be handled inside write function and application should not care.
这几乎就是 write() 现在的工作方式 - 它确实处理缓冲并且让应用程序不关心,无论是好是坏。另见 this answer了解更多信息。
Reading the answer and links again, I think the the functions work like this.
write() 还是比那个聪明一点。它不会尝试只写一次,它实际上会安排数据继续写入,直到没有数据可写为止。即使您从不 await 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()
实际实现起来比较复杂,因为:
os.write;drain() 并没有真正等到缓冲区为空,而是直到它到达 low watermark。 ;_do_write 中引发的 EWOULDBLOCK 以外的异常被存储并在 drain() 中重新引发。最后一点是调用 drain() 的另一个很好的理由 - 实际注意到对等点由于写入失败而消失了。
关于python - 为什么要显式调用 asyncio.StreamWriter.drain?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53779956/
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返
它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput
我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?