> For the complete documentation index, see [llms.txt](https://go.raymondctw.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://go.raymondctw.dev/technology/python/speed-up-python/asynchronous.md).

# Asynchronous

在一開始開發程式時，「自動」執行批次任務相對比「手動」下，怎麼樣都覺得很有效率，但當程式開發到一定程度時，某些專案就會開始要求速度，例如我近幾個月都在開發選擇權造市程式，這個時候執行速度就是相當重要啦！

一般正常狀態撰寫的程式是由上而下的線性執行，當執行無誤完成第一步後，才會接著執行下一步。如果任務之間沒有相依性，我們可以如何增加效率呢？

我們先舉一個生活上的例子——「煮飯」，近期作為外派菜鳥的我正在學習如何自己煮飯，畢竟在台灣的都會區生活實在太方便了，到了異國只能自己來才吃得到想吃的味道。而我目前的階段屬於煮飯菜鳥，一次不能做太多事，免得手忙腳亂全部搞砸，所以一次只能做一件事情，因為很多事情都沒做過也不知道時間處理會如何。

#### Lv 1 廚房菜鳥：(Single thread)

* 一次處理一件任務：例如先洗菜，再煮一鍋水，再把菜放下去燙；洗米、泡米、再放入電鍋煮。
* 假設每件任務都是獨立且依序執行

#### Lv 2 廚房進階菜鳥：(Asynchronous)

* 區別什麼任務會花比較久的時間，但又不用自己實際盯場，例如電鍋煮飯。
* 當在執行等待任務完成的時間，就可以先去做其他任務，例如洗完米按下電鍋後，就可以來準備煎荷包蛋或是其餘備料，等飯煮好時，再回來處理飯的其他流程。

#### Lv 3 廚房老手：(Multiple threads & Asynchronous)

* 可以區分什麼任務會花比較久的等待時間。
* 可同時處理多項任務，例如可以同時煮麵跟煎荷包蛋，但必須要有多個瓦斯爐或鍋子。

> :joy: 這些都是我在煮飯時悟出的程式執行道理！

回到Python的世界，我們先使用廚房菜鳥的模式，我們的範例是煮咖啡與烤貝果，如下面的範例，煮咖啡要3分鐘，而烤貝果則需要5分鐘，如果我們一項接著一項做，總共需要8分鐘左右才能吃到一個早餐組合。

#### 一般模式（非使用Async)：

```python
import time

def brew_coffee():
    print("Start brew coffee")
    time.sleep(3 * 60)
    print("End brew coffee")
    return "coffee ready"
    
def toast_bagel():
    print("Start toast bagel")
    time.sleep(5 * 60)
    print("End toast bagel")
    return "bagel toasted"
    
def main():
    start_time = time.time()
    
    result_coffee = brew_coffee()
    result_bagel = toast_bagel()
    
    end_time = time.time()
    elapsed_time = end_time - start_time
    
    print(f"Result of brew_coffee: {result_coffee}")
    print(f"Result of toast bagel: {result_bagel}")
    print(f"Total execution time: {elapsed_time:2f} seconds")

if __name__ == "__main__":
    main()
```

第二種模式比較屬於正常人的模式，我們知道咖啡機可能要花一些時間預備，所以啟動後，這時候我們也趕快把貝果從冰箱拿出來放到烤箱，並將時間設定5分鐘，就讓烤箱開始烤，而咖啡機這時候可能也差不多好了，再回到咖啡機去處理磨豆等任務，當烤箱叮的一聲完成後，咖啡與烤貝果等任務其實也完成了，這個就是Async的概念。

#### Async模式

```python
import asyncio
import time

async def brew_coffee():
    print("Start brew coffee")
    await asyncio.sleep(3 * 60)
    print("End brew coffee")
    return "coffee ready"
    
async def toast_bagel():
    print("Start toast bagel")
    await asyncio.sleep(5 * 60)
    print("End toast bagel")
    return "bagel toasted"
    
async def main():
    start_time = time.time()
    
    # method 1: asyncio.gather()
    batch = asyncio.gather(brew_coffee(), toast_bagel())
    result_coffee, result_bagel = await batch
    
    # method 2: asyncio.create_task()
    # coffee_task = asyncio.create_task(brew_coffee())
    # toast_task = asyncio.create_task(toast_bagel())
    
    # result_coffee = await coffee_task
    # result_toast = await toast_task
    
    end_time = time.time()
    elapsed_time = end_time - start_time
    
    print(f"Result of brew_coffee: {result_coffee}")
    print(f"Result of toast bagel: {result_bagel}")
    print(f"Total execution time: {elapsed_time:2f} seconds")
    
if __name__ == "__main__":
    asyncio.run(main())
```

Asynchronous是近年比較常使用的加速處理模式，主要用來加速多項耗時較長的任務，例如資料庫I/O操作與爬蟲等任務，而這兩項任務都是等待其他服務的回應，而非本機端在進行處理。所以想透過Async加速的第一步就是識別專案的任務有哪些，個別分析耗時的狀況。

另一個系列加速處理的就是multiprocessing和threading，這兩項屬於平行同步處理，可能會導致一些資源消耗或併發一些問題，例如瞬間CPU達到極限或是記憶體佔用率過高，使得全部程序都停擺，而我自己的選擇權造市則使用混合模式執行。

***

### Reference

1. [Socratica - AsyncIO, await, and async - Concurrency in Python](https://www.youtube.com/watch?v=K56nNuBEd0c)

{% embed url="<https://www.youtube.com/watch?v=K56nNuBEd0c>" %}
