iT邦幫忙

2023 iThome 鐵人賽

DAY 21
0

Python從3.5版本開始加入了async和await關鍵字,讓我們可以用非同步的方式來編寫程式。非同步程式的好處是可以在等待IO操作時不會阻塞主執行緒,從而提高程式運行效率。

要使用async/await,我們需要先將函式宣告為async def,就可以在函式內使用await。await用在非同步函式呼叫前,會暫停執行直到呼叫完成,再繼續往下執行。舉例:

import asyncio

async def fetch_data():
    print('start fetching')
    await asyncio.sleep(2) # 模擬IO阻塞的操作
    print('done fetching')
    return {'data': 1}

async def print_number():
    for i in range(10):
        print(i)
        await asyncio.sleep(0.25)

async def main():
    task1 = asyncio.create_task(fetch_data()) # 建立task1
    task2 = asyncio.create_task(print_number()) # 建立task2

    print('start')
    await task1 # 等待task1完成
    print('continue')
    await task2 # 等待task2完成    

asyncio.run(main())

這個例子中,main函式使用await等待task1和task2完成。在等待時,主執行緒不會被block,可以去執行print和其他尚未await的程式碼。這同時也被稱為協程。

我們可以看到,非同步程式可以發揮Python的多執行緒效能,並避免執行緒間的鎖死問題。Asyncio模組提供了非同步IO和事件循環,是實作非同步程式的重要工具。

更多資訊可見 https://docs.python.org/zh-tw/3/library/asyncio.html


同樣的在discord.py中也利用了asyncio來實現非阻塞的 asynchronous 事件循環。

Discord.py中的Bot是一個asyncio.Client,代表一個連線到Discord API的非同步客戶端。 我們可以透過discord.py來監聽事件並執行非同步函式,例如:

import discord

client = discord.Client()

@client.event
async def on_ready():
    print('Logged in as')
    print(client.user.name)

@client.event
async def on_message(message):
    if message.content.startswith('!hello'):
        await message.channel.send('Hello!')
    await client.process_commands(message)

client.run('token')

在這裡,on_ready和on_message都是非同步函式,會與Discord API形成一個事件循環。

當Bot完成登入時,on_ready會被呼叫;當收到訊息時,on_message會被呼叫。 我們用await來呼叫非同步的API如send(),這樣Bot就可以持續接收其他事件,而不會被block住。

所以透過discord.py和asyncio,我們可以方便地用非同步的方式來開發Discord 機器人了。


因此我們也可以在discord.py程式中使用自己寫的協程函式如下:

  1. 將自定義的async函數放在一個文件中,例如utils.py
# utils.py

import asyncio

async def my_async_func():
    await asyncio.sleep(1)
    print('Done!')
  1. 在機器人程式文件中import該文件並使用
# bot.py

import discord
from utils import my_async_func

client = discord.Client()

@client.event
async def on_ready():
    print('Ready!')  

@client.event
async def on_message(message):
    if message.content == '!mycmd':
        await my_async_func()

client.run('token') 
  1. 在on_message事件中檢測指令並呼叫自定義的async函數

  2. 使用await來呼叫該函數,以非同步的方式執行

  3. my_async_func會在背景執行,不會阻塞on_message繼續處理其他訊息

這樣就可以將自定義的async程式碼整合到discord.py機器人中,並發揮非阻塞異步編程的優勢了。


上一篇
[DAY20]Discord bot訊息保存
下一篇
[DAY22]補充: MultiThreading 多線程 & MultiProcessing 多進程
系列文
selenium爬蟲應用至discord bot30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言