我已经看过几个关于asyncio的基本Python 3.5教程,它们以各种方式执行相同的操作.在这段代码中:
import asyncio
async def doit(i):
print("Start %d" % i)
await asyncio.sleep(3)
print("End %d" % i)
return i
if __name__ == '__main__':
loop = asyncio.get_event_loop()
#futures = [asyncio.ensure_future(doit(i), loop=loop) for i in range(10)]
#futures = [loop.create_task(doit(i)) for i in range(10)]
futures = [doit(i) for i in range(10)]
result = loop.run_until_complete(asyncio.gather(*futures))
print(result)
Run Code Online (Sandbox Code Playgroud)
上面定义futures变量的所有三个变体都实现了相同的结果; 我能看到的唯一区别是,对于第三个变体,执行是乱序的(在大多数情况下这应该无关紧要).还有其他区别吗?有些情况下我不能只使用最简单的变体(协程的简单列表)吗?
Kotlin协程可以用于生产,它们的实验状态意味着什么?
我怀疑它可以移植,但有没有解决方案?我认为可以通过创建备用堆栈并在功能输入上重置SP,BP和IP,并使产量保存IP并恢复SP + BP来完成.析构函数和异常安全看起来很棘手但可以解决.
它完成了吗?这不可能吗?
Monads可以做许多惊人的,疯狂的事情.他们可以创建具有值叠加的变量.它们可以允许您在计算之前访问未来的数据.它们可以让您编写破坏性更新,但不是真的.然后延续monad让你打破人们的思想!通常是你自己的.;-)
但这是一个挑战:你能制作一个可以暂停的单子吗?
data Pause s x instance Monad (Pause s) mutate :: (s -> s) -> Pause s () yield :: Pause s () step :: s -> Pause s () -> (s, Maybe (Pause s ()))
该Pause单子是一种状态的单子(因此mutate,具有明显的语义).通常情况下,像这样的monad具有某种"运行"功能,它运行计算并将您送回最终状态.但Pause它是不同的:它提供了一个step函数,它运行计算直到它调用魔法yield函数.这里计算暂停,返回给调用者足够的信息以便稍后恢复计算.
额外的awesomness:允许调用者修改step调用之间的状态.(例如,上面的类型签名应该允许这样做.)
使用案例:编写执行复杂操作的代码通常很容易,但是要将其转换为也在其操作中输出中间状态的总PITA .如果您希望用户能够在执行过程中途改变某些内容,那么事情变得非常复杂.
实施思路:
显然它可以用线程,锁和IO.但我们能做得更好吗?;-)
继续monad疯狂的东西?
也许是某种编写器monad,yield只记录当前状态,然后我们可以step通过迭代日志中的状态来"假装" 它.(显然这排除了改变步骤之间的状态,因为我们现在并没有真正"暂停"任何东西.)
可以说我有两个功能:
def foo():
return 'foo'
def bar():
yield 'bar'
Run Code Online (Sandbox Code Playgroud)
第一个是正常函数,第二个是生成器函数.现在我想写这样的东西:
def run(func):
if is_generator_function(func):
gen = func()
gen.next()
#... run the generator ...
else:
func()
Run Code Online (Sandbox Code Playgroud)
什么是直截了当的实现is_generator_function()?使用types包我可以测试是否 gen是生成器,但我希望在调用之前这样做func().
现在考虑以下情况:
def goo():
if False:
yield
else:
return
Run Code Online (Sandbox Code Playgroud)
调用goo()将返回生成器.我假设python解析器知道该goo()函数有一个yield语句,我想知道是否可以轻松获取该信息.
谢谢!
背景:
我问这个是因为我目前有一个包含许多(数百到数千)个线程的应用程序.这些线程中的大多数在很长一段时间内处于空闲状态,等待将工作项放入队列中.当工作项可用时,然后通过调用一些任意复杂的现有代码来处理它.在某些操作系统配置中,应用程序会遇到控制最大用户进程数的内核参数,因此我想尝试减少工作线程数的方法.
我建议的解决方案
它似乎是一个基于协程的方法,我用协程替换每个工作线程,将有助于实现这一目标.然后,我可以拥有一个由实际(内核)工作线程池支持的工作队列.当项目被放置在特定协程的队列中进行处理时,会将一个条目放入线程池的队列中.然后它将恢复相应的协同程序,处理其排队的数据,然后再次暂停它,释放工作线程以执行其他工作.
实施细节:
在考虑如何做到这一点时,我无法理解无堆栈和堆栈协程之间的功能差异.我有一些使用Boost.Coroutine库的堆栈协同程序的经验.我发现从概念层面理解它相对容易:对于每个协同程序,它维护CPU上下文和堆栈的副本,当你切换到协程时,它切换到保存的上下文(就像内核模式调度程序一样) ).
对我来说不太清楚的是无堆栈协程与此有何不同.在我的应用程序中,与上述工作项排队相关的开销量非常重要.我见过的大多数实现,比如新的CO2库,都表明无堆栈协程提供了更低开销的上下文切换.
因此,我想更清楚地理解无堆栈和堆栈协程之间的功能差异.具体来说,我想到了这些问题:
像这样的参考文献表明,区别在于你可以在一个堆栈与无堆栈协程中产生/恢复.是这样的吗?有一个简单的例子,我可以在一个堆栈的协程中做但不能在无堆栈的协同程序中吗?
使用自动存储变量(即"堆栈中的变量")是否有任何限制?
我可以从无堆协程中调用哪些函数有任何限制吗?
如果无堆栈协程没有保存堆栈上下文,那么当协同程序运行时,自动存储变量会去哪里?
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import time
async def foo():
await time.sleep(1)
foo()
Run Code Online (Sandbox Code Playgroud)
我无法使这个简单的例子运行:
RuntimeWarning: coroutine 'foo' was never awaited foo()
Run Code Online (Sandbox Code Playgroud) 我已经在CPython中读到过,解释器堆栈(调用到达这一点的Python函数列表)与C堆栈(在解释器自己的代码中调用的C函数列表)混合在一起.如果是这样,那么生成器和协同程序是如何实现的呢?他们如何记住自己的执行状态?CPython是否将每个生成器/ coroutine的堆栈复制到OS堆栈中?或者CPython只是将生成器的最顶层堆栈帧保留在堆上,因为生成器只能从最顶层的帧生成?
协同程序和资源获取的结合似乎可能会产生一些意想不到的(或不直观的)后果.
基本问题是这样的事情是否有效:
def coroutine():
with open(path, 'r') as fh:
for line in fh:
yield line
Run Code Online (Sandbox Code Playgroud)
它做的.(你可以测试一下!)
更深层次的关注是with应该是替代方案finally,在此确保资源在块结束时释放.协同程序可以暂停和恢复执行中的with块,所以如何在冲突解决?
例如,如果在协程内部和外部打开一个带有读/写的文件,而协程尚未返回:
def coroutine():
with open('test.txt', 'rw+') as fh:
for line in fh:
yield line
a = coroutine()
assert a.next() # Open the filehandle inside the coroutine first.
with open('test.txt', 'rw+') as fh: # Then open it outside.
for line in fh:
print 'Outside coroutine: %r' % repr(line)
assert a.next() # Can we still use …Run Code Online (Sandbox Code Playgroud) coroutine ×10
python ×5
c++ ×2
python-3.5 ×2
python-3.x ×2
async-await ×1
c++17 ×1
concurrency ×1
free-monad ×1
function ×1
generator ×1
haskell ×1
kotlin ×1
monads ×1
resources ×1
use-case ×1