【问题标题】:python generators for concurrency用于并发的python生成器
【发布时间】:2014-07-05 11:14:43
【问题描述】:

我正在关注 Python 大师 David Beazley 的幻灯片。它指出“生成器也用于并发。这是一个示例:

from collections import deque

def countdown(n):
    while n > 0:
        print("T-minus", n)
        yield
        n -=1

def countup(n):
    x = 0
    while x > n:
        print("Up we go", x)
        yield
        x +=1

# instantiate some tasks in a queue
tasks = deque([countdown(10),
               countdown(5),
               countup(20)
               ])

# run a little scheduler
while tasks:
    t = tasks.pop()  # get a task
    try:
        next(t)   # run it until it yields
        tasks.appendleft(t) # reschedule
    except StopIteration:
        pass

这是输出:

T-minus 5
T-minus 10
T-minus 4
T-minus 9
T-minus 3
T-minus 8
T-minus 2
T-minus 7
T-minus 1
T-minus 6
T-minus 5
T-minus 4
T-minus 3
T-minus 2
T-minus 1

问题是生成器是如何引入并发的,它是如何体现的?

【问题讨论】:

  • 在该示例中,并发性很简单(对我而言)。也许问题在于您对术语并发的理解?
  • 请用外行的方式解释并发,以便我和像我这样的其他人更好地理解。我认为这是一个接一个的任务,因为它们被删除然后放回队列中
  • 如果你还没有,你可以试试 PyCon 演讲,这些幻灯片来自:youtube.com/watch?v=MCs5OvhV9S4

标签: python asynchronous concurrency generator


【解决方案1】:

这段代码实现了“绿色线程”、协作、用户态(相对于抢占式、内核)线程的概念。

“线程”是生成器,每个函数都包含yeildyield from。显然,调度程序存在于if __name__ == '__main__': 位中。

因此,假设我们没有生成器,而是常规列表,每个列表中都有一系列函数。

def doThis(): pass
def sayThis(): pass
def doThat(): pass
...

myThread = [doThis, doThat, doAnother]
yourThread = [sayThis, sayThat, sayAnother]

我们可以按顺序运行所有函数:

for thread in [myThread, yourThread]:
    for stmt in thread:
        stmt()

或者我们可以按其他顺序进行:

for myStmt, yourStmt in zip(myThread, yourThread):
    myStmt()
    yourStmt()

在第一个“调度程序”中,我们耗尽了第一个线程,然后继续执行第二个线程。在第二个调度程序中,我们将两个线程中的语句交错,首先是我的,然后是你的,然后再回到我的。

这是因为我们在耗尽这些线程之前在多个“线程”之间交错“语句”,我们可以说第二个调度程序提供了并发性。

请注意,并发并不一定意味着并行。这不是同时执行,只是重叠。

【讨论】:

  • 就像另一个问题一样。如果生成器可以帮助实现并发,那么真的需要线程吗?如果是,为什么?如果不是,为什么不呢?非常感谢
  • 编程中的很多东西并不是绝对必要的,例如:variables,但是它们很方便,所以我们还是保留了它们。
【解决方案2】:

这是一个澄清的例子:

from collections import deque

def coro1():
    for i in range(1, 10):
        yield i

def coro2():
    for i in range(1, 10):
        yield i*10

print('Async behaviour'.center(60, '#'))
tasks = deque()
tasks.extend([coro1(), coro2()])

while tasks:
    task = tasks.popleft()  # select and remove a task (coro1/coro2).
    try:
        print(next(task))
        tasks.append(task)  # add the removed task (coro1/coro2) for permutation.
    except StopIteration:
        pass

输出:

######################Async behaviour#######################
1
10
2
20
3
30
4
40
5
50
6
60
7
70
8
80
9
90

【讨论】:

    猜你喜欢
    • 2018-11-01
    • 2017-08-03
    • 2011-12-19
    • 2020-03-15
    • 2013-05-31
    • 2013-10-02
    • 2019-04-19
    • 1970-01-01
    • 2011-01-09
    相关资源
    最近更新 更多