可以同时运行2个for循环,一个又一个循环吗?

时间:2019-03-25 04:16:46

标签: python multithreading loops mpi theory

我只是想知道,如何创建一个循环,使每次迭代都一次又一次地发生?我知道多线程是一回事,我对此很熟悉。我不知道的一件事是如何一个接一个地运行循环。

例如,假设我有2个功能:

def loop_a():
    while True:
        time.sleep(1)
        print("a")

def loop_b():
    while True:
        print("b")

即使第一个功能中包含ababababababababa,如何将输出设为time.sleep(1)

我正在使用 mpi4py ,并且想知道是否可以使用此库来执行此操作。 我的实际程序要求在函数之间发送消息。否则,使用任何其他python库(例如multiprocessing都可以)。

是否可以使用线程来做到这一点?

3 个答案:

答案 0 :(得分:0)

您可以使用corutines:

import asyncio

q = asyncio.Queue()

async def loop_a(q):
  for i in range(10):
    value = await q.get()
    print(value)

async def loop_b(q):
  for i in range(10):
    await q.put("a")
    print("b")


loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(loop_a(q), loop_b(q)))

这里有live example

唯一的想法是,除非您以某种方式同步它们,否则无法保证执行顺序。

答案 1 :(得分:0)

这是问题第一部分的解决方案-如何并行运行进程,以便每个进程等待上一个进程完成后才能开始处理任务。我在这里没有解决消息传递方面的问题,因为对我来说似乎有点含糊,可以根据问题说明以不同的方式实施。在此示例中,我们创建并运行三个工作程序,它们通过简单的时间延迟来模拟执行。代码段应保存在单个文件中,该文件可以从命令行运行。

我们首先导入所需的模块:

#!/usr/bin/env python3
import time

from multiprocessing import Process, Event

并实现WorkerQueue类。此类使工人保持正确的秩序,并负责启动和终止他们。工人之间的交流是使用事件实现的。每个工作人员都有 other_ready ready Event字段,分别指示前一个工作人员和当前工作人员的完成状态。请注意,如果队列中只有一个工作线程,则其 other_ready ready 相同。

class WorkerQueue(object):

    def __init__(self):
        self._workers = []

    def add_worker(self, worker):

        if self._workers:
            worker.other_ready = self._workers[-1].ready
            self._workers[0].other_ready = worker.ready
        else:
            worker.other_ready = worker.ready

        self._workers.append(worker)

    def start_workers(self):

        if not self._workers:
            return

        self._workers[0].other_ready.set()

        for w in self._workers:
            w.start()

    def stop_workers(self):

        for w in self._workers:
            w.join()

然后,我们通过继承Process类来实现worker本身。请注意,也可以使用threading代替multiprocessing。在这种情况下,唯一改变的是Worker父类Thread而不是Process

class Worker(Process):

    def __init__(self, delay, name=None):
        super().__init__(name=name)
        self.delay = delay
        self.other_ready = Event()
        self.other_ready.set()
        self.ready = Event()
        self.stop = Event()

    def run(self):

        while not self.stop.is_set():

            try:
                self.other_ready.wait()

                t = time.strftime('%H:%M:%S')
                print('Started:', self.name, t, flush=True)

                time.sleep(self.delay)

                t = time.strftime('%H:%M:%S')
                print('Finished:', self.name, t, flush=True)
            except:
                break

            self.other_ready.clear()
            self.ready.set()

    def join(self, timeout=None):
        self.stop.set()
        super().join(timeout)

在这里,您看到每个工作人员在开始执行命令之前等待上一个工作人员准备就绪。默认情况下,已设置 other_ready ,这样我们就不会在队列中只有一个工人的情况下陷入僵局。

最后,我们实现一个main函数,其中定义了工作程序,将它们添加到工作程序队列中,然后启动它们。

def main():
    first = Worker(delay=1, name='first')
    second = Worker(delay=3, name='second')
    third = Worker(delay=2, name='third')

    queue = WorkerQueue()

    for w in (first, second, third):
        queue.add_worker(w)

    queue.start_workers()

    try:

        # The main infinite loop, do something useful:
        while True:
            time.sleep(1)

    except KeyboardInterrupt:
        pass
    finally:
        queue.stop_workers()

不要忘记在文件末尾添加以下行:

if __name__ == '__main__':
    main()

现在,可以将其保存到文件proc_queue.py中了,您可以从命令行运行该文件以查看结果:

$ python3 proc_queue.py 
Started: first 16:04:09
Finished: first 16:04:10
Started: second 16:04:10
Finished: second 16:04:13
Started: third 16:04:13
Finished: third 16:04:15
Started: first 16:04:15
Finished: first 16:04:16
Started: second 16:04:16
Finished: second 16:04:19
Started: third 16:04:19
Finished: third 16:04:21
^C

这可能有点复杂,但这是我能想到的唯一解决方案。如果您知道更好的方法,我很乐意了解:)

答案 2 :(得分:0)

使用伪代码:

main()
1. set lock for loop1
2. start loop1 on background thread
3. start loop2 on background thread
4. wait

loop1()
1. do the following forever:
2.    acquire lock for loop1
3.    print 'a'
4.    release lock for loop2

loop2()
1. do the following forever:
2.    acquire lock for loop2
3.    print 'b'
4.    release lock for loop1

您可以将锁实现为共享内存变量,也可以实现为等待从对等方或其他任何人获取消息的循环。获取锁将意味着锁定或旋转锁定(轮询),直到准备就绪为止。释放锁定将是适当地设置共享变量或将正确的消息发送给正确的对等方。

编辑:基于评论,这是使用许多可用的实现策略之一对loop1()和loop2()进行更全面的开发:

(shared lock in global scope)

main()
1. lock = 1
2. start loop1 on background thread
3. start loop2 on background thread
4. wait

loop1()
1. do the following forever
2.    loop until lock = 1
3.    print 'a'
4.    lock = 2

loop2()
1. do the following forever
2.    loop until lock = 2
3.    print 'b'
4.    lock = 1

此实现使用自旋锁,并依赖于线程安全的共享变量lock来协调工作。自旋锁可能适合或不适合您的应用。您可以将它们与某些阻止机制结合使用,以减少处理时间,但会导致处理延迟。

关键是lock是有状态的,并且(应该)仅由正确的线程获取。如果使每个线程“了解”“下一个”线程并在完成时向其发送消息,然后所有线程都等待消息通知,则可以对消息传递执行相同的操作。

main()
1. start loop1 on background thread
2. start loop2 on background thread
3. message loop1
4. wait

loop1()
1. do the following forever
2.    loop until message received
3.    print 'a'
4.    message loop2

loop2()
1. do the following forever
2.    loop until message received
3.    print 'b'
4.    message loop1