两个无限循环交替?

时间:2018-01-22 23:01:03

标签: python multithreading python-3.x python-multithreading

我正在寻找这个问题的解决方案:

我有两个功能:

def foo1():
    while True:
        print("x")

def foo2():
    while True:
        print("y")

我想交替运行它们,例如:

  1. 运行foo1()
  2. 停止foo1()运行foo2()
  3. 停止foo2()运行foo1()
  4. 功能无法同时工作,目前只有其中一项可以正常工作。

5 个答案:

答案 0 :(得分:5)

编辑:留给@StefanPochmann拍下我的漂亮答案:这个问题无关紧要,但肯定值得一提的是拉链工作配对对。因此,虽然您可以在奇数次迭代后正式暂停下面定义的alter,但此时“y”将已经打印出来。

如果您关心这个问题,那么Stefan的解决方案也是

>>> alter = map(next, it.cycle((foo1(), foo2())))

实际上非常聪明。

编辑结束

您可以将函数转换为生成器,然后压缩它们。

>>> import itertools as it
>>>
>>> def foo1():
...     while True:
...         print("x")
...         yield None
... 
>>> def foo2():
...     while True:
...         print("y")
...         yield None
... 
>>> alter = it.chain.from_iterable(zip(foo1(), foo2()))
>>> while True:
...     next(alter)

x
y
x
y
...

或:

>>> for a in alter:
...     pass

x
y
x
y
...

答案 1 :(得分:3)

我个人赞成Paul Panzer的itertools方法。但是,如果它是一个选项,我强烈建议将“无限循环”移出这些函数,而是将它放在另一个“驱动程序”函数中,以交替的方式调用这两个函数。

def f1():
    print('x')

def f2():
    print('y')

def driver():
    while True:
        f1()
        f2()

我认为这在任何情况下都无法实现,但如果可以的话,它最终会导致更好的设计。

答案 2 :(得分:1)

根据你所说的你不想要/他们需要它们真的同时运行,而是能够在它们之间来回切换执行。您所描述的是co-operative multitasking所称的内容。

这可以通过简单地使每个函数成为coroutine来实现。在Python中,只需在正确的位置向它们添加yield语句即可完成它们generator functions。这样他们每次暂停执行,直到再次调用next()为止。

唯一有点棘手的部分是记住每个发电机功能都需要被引导"一旦它被第一次调用(实际上它不执行函数中的代码)。第一次调用函数本身返回的值next()的第一次调用会将其执行推进到其中第一个yield的位置。

这就是我的意思:

def foo1():
    while True:
        yield
        print("x")

def foo2():
    while True:
        yield
        print("y")

# Sample usage.
def process():
    f1 = foo1()
    next(f1)  # Prime generator.
    f2 = foo2()
    next(f2)  # Prime generator.
    while True:
        next(f1)
        next(f2)


process()

输出:

x
y
x
y
x
...

如果您要做很​​多这样的事情,您可以创建一个装饰器,使生成器函数用作协程自动启动(取自David Beazley在Pycon 2009上给出的标题为Curious Course on Coroutines and Concurrency的教程) )。

def coroutine(func):
    """ Decorator for wrapping coroutine functions so they automatically
        prime themselves.
    """
    def start(*args,**kwargs):
        cr = func(*args,**kwargs)
        next(cr)
        return cr

    return start

@coroutine
def foo1():
    while True:
        yield
        print("x")

@coroutine
def foo2():
    while True:
        yield
        print("y")

# Sample usage.
def process():
    f1, f2 = foo1(), foo2()  # Each will be "primed" automatically.
    for _ in range(10):
        next(f1)
        next(f2)

答案 3 :(得分:1)

使用您已经展示的功能,无法做您想做的事情。但是,有些方法可能会在不同程度上接近,具体取决于您在当前代码中愿意更改的详细信息。

获得并行执行排序的一个选择是使用线程。 Python实际上并不允许多个线程同时运行Python代码,因此在大多数情况下这并不是很有用,但对于您的特定用例可能已经足够了。不幸的是,它对你当前的代码非常糟糕,因为一个线程会长时间阻塞另一个线程,所以你会得到很多x s,然后是很多y s ,因为它们的功能只是不规则地交换GIL。

另一种选择是使用multiprocessing,它允许并行执行。它可以更好地交错两个函数,但两个进程之间交换的确切时间仍然不会完全一致,所以你不会总是得到" xyxyxyxy",你可能会得到更像是" xyxxyyxy" (具体细节可能取决于您的操作系统,CPU以及运行代码时机器的繁忙程度。)

现在我们进入需要修改功能的解决方案。一个显而易见的解决方案是简单地将它们重写为执行这两个步骤的单个函数:

def foo_combined():
    while True:
        print('x')
        print('y')

另一种选择是将函数转换为生成器,您可以使用zip

自行组合
def foo1_gen():
    while True:
        yield 'x'

def foo2_gen():
    while True
        yield 'y'

您可以使用这样的生成器:

for x, y in zip(foo1_gen(), foo2_gen()):
    print(x)
    print(y)

或者,如果你想要一个可迭代:

for val in itertools.chain.from_iterable(zip(foo1_gen(), foo2_gen())):
    print(val)

答案 4 :(得分:0)

所以这是我个人的做法,但可能更好。

def foo(i):
    while(True):
       i += 1
       if(i%2 == 0):
            print('x')
       else:
            print('y')

所以它的工作方式是i作为迭代器。然后它检查它每次使用i+= 1递增迭代器1。接下来,我们检查i是奇数还是偶数,因为它会交替。因此,对于每个偶数,您将获得x,而每个奇数,您将获得y。希望这有帮助!