簡體   English   中英

如何在 python 中實現給定兩個協程,保持運行一個直到另一個完成

[英]How to implement in python given two coroutine, keep run one until the other finished

python asyncio lib 非常新,並且一直在努力實現保持活動狀態的任務。 我想同時運行一個 cpu 密集型任務和一個保活任務。 保持活動應該定期運行,直到 cpu 密集型完成。

import asyncio
import time

async def cpu_intensive():
    print("cpu_intensive for 3 seconds")
    delay = 3
    close_time = time.time() + delay
    while True:
        if time.time() > close_time:
            break


async def keep_alive():
    print("keep alive for 1 second") # My real use case is I want to send a heart beat message every x seconds until cpu intensive finished
    await asyncio.sleep(1)

async def main():
    cpu_intensive_task = asyncio.create_task(cpu_intensive())
    keep_alive_task = asyncio.create_task(keep_alive())
    print(f"Started at {time.strftime('%X')}")
    # TODO: Not sure how to achieve the expected output
    print(f"Finished at {time.strftime('%X')}")

asyncio.run(main())

'''
Expected Output
Started at 23:55:08
cpu_intensive 3 seconds
keep alive for 1 seconds
keep alive for 1 seconds
keep alive for 1 seconds
Finished at 23:55:11
'''

我瀏覽了 asyncio lib python 並嘗試了幾個 API 例如awaitrun_coroutine_threadsafeasyncio.gather 但無法讓它工作。

您是否嘗試過使用線程? 您可以像這樣輕松實現它。

import threading
import time
from datetime import datetime

def cpu_intensive():
    time.sleep(10) #this is the time consuming task
    print("CPU INTENSTIVE TASK DONE")

def keep_alive():
    now = datetime.now() # current date and time
    date_time = now.strftime("%m/%d/%Y, %H:%M:%S")
    print(f"{date_time}  -- BUSY STATUS / SIGNAL CODE")

#Starting the cpu_intensive task
thread = threading.Thread(target=cpu_intensive)
now = datetime.now() # current date and time
date_time = now.strftime("%m/%d/%Y, %H:%M:%S")
print("STARTING CPU PROCESS: ", date_time) 
thread.start()

# Doing something while the task is alive
while thread.is_alive():
    keep_alive()
    time.sleep(1)
thread.join()
print("TASK COMPLETE")

output 應如下所示

STARTING CPU PROCESS:  11/18/2021, 14:53:05
11/18/2021, 14:53:05  -- BUSY STATUS / SIGNAL CODE
11/18/2021, 14:53:06  -- BUSY STATUS / SIGNAL CODE
11/18/2021, 14:53:07  -- BUSY STATUS / SIGNAL CODE
11/18/2021, 14:53:08  -- BUSY STATUS / SIGNAL CODE
11/18/2021, 14:53:09  -- BUSY STATUS / SIGNAL CODE
11/18/2021, 14:53:10  -- BUSY STATUS / SIGNAL CODE
11/18/2021, 14:53:11  -- BUSY STATUS / SIGNAL CODE
11/18/2021, 14:53:12  -- BUSY STATUS / SIGNAL CODE
11/18/2021, 14:53:13  -- BUSY STATUS / SIGNAL CODE
11/18/2021, 14:53:14  -- BUSY STATUS / SIGNAL CODE
CPU INTENSTIVE TASK DONE
TASK COMPLETE

您也可以切換線程以保持 keep_alive。

我認為您可能將並發的概念與並行性混淆了。 讓我寫一些我在玩 asyncio 時的理解:

實現並發的兩種方式

  • Parallel :“物理”並發,其中可能有超過 1 行代碼同時執行。

    • multiprocessing
    • pro : 可以使用多核
    • con :需要一些資源來創建進程。 與進程通信的高開銷(使用pickle序列化)。 工作負載必須是線程安全的。
  • Asynchronous(await/async) :“感知”並發,在任何給定時間執行的代碼不能超過 1 行,但通過上下文切換實現並發。 使用await關鍵字來允許上下文更改。

    • asyncio curio trio
    • pro :可以比同步代碼更好地利用一個內核。 輕了很多。 控制流比線程更可預測。
    • con : 不能使用多核 在任何給定時間都不能運行超過 1 個代碼。 無法在繁重的工作量中切換上下文。
  • Asynchronous(time division) :又名線程。 由於GIL ,python 中的線程在任何給定時間只能執行 1 行代碼。 因此與上述有相似之處。

    • threading
    • pro :可以比同步代碼更好地利用一個內核。 (因為它在等待時運行其他代碼。)非常輕量級。 由於它使用時分方法,即使在 CPU 繁重的工作負載下也可以運行。 (通過短暫停止工作負載並執行其他線程)
    • con : 不能使用多核 在任何給定時間都不能運行超過 1 個代碼。 控制流很難預測。

因此,對於任何 CPU 密集型工作負載,最好是並行化。

對於任何 IO 綁定工作負載(又名等待),最好異步編碼 - 因為我們不需要使用更多內核。

代碼修復

你需要在cpu_intensive協程中await一些東西。

如這篇SO 帖子所示,我們可以使用yield asyncio.sleep(0)在工作負載中添加上下文切換點。 當然,這不是編寫異步代碼的理想方式,但如果您需要將此類 function 附加到異步代碼,這是一種方式。

另外,您需要在給定任務實例運行時循環keep_alive ,該檢查由asyncio.Task.done()完成。

import asyncio
import time


async def cpu_intensive():
    print("cpu_intensive for 3 seconds")
    duration = 3
    close_time = time.time() + duration
    while True:
        if time.time() > close_time:
            break

        await asyncio.sleep(0)


async def keep_alive(task: asyncio.Task):
    while not task.done():
        print("keep alive for 1 second")
        # My real use case is I want to send a heart beat message every x seconds until cpu intensive finished
        await asyncio.sleep(1)


async def main():
    print(f"Started at {time.strftime('%X')}")

    cpu_intensive_task = asyncio.create_task(cpu_intensive())
    keep_alive_task = asyncio.create_task(keep_alive(cpu_intensive_task))

    await keep_alive_task
    
    print(f"Finished at {time.strftime('%X')}")

asyncio.run(main())

'''
Output:
Started at 16:16:42
cpu_intensive for 3 seconds
keep alive for 1 second
keep alive for 1 second
keep alive for 1 second
keep alive for 1 second
Finished at 16:16:46
'''

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM