0. 背景
想了解一下 Python 的异步编程,所以就学习了一些示例代码。
1. 示例代码 1
controllers.py
import asyncio
from datetime import datetime
async def foo():
print(f'{datetime.now()} Foo')
async def bar():
while True:
print(f'{datetime.now()} Bar')
await asyncio.sleep(1)
代码说明,
这是一个使用 asyncio 库实现的 Python 异步编程示例。asyncio 是 Python 3.4 引入的标准库,用于编写协程(coroutine)和异步 I/O 代码。在这个示例中,定义了两个异步函数 foo 和 bar。
foo 函数是一个简单的异步函数,每次被调用时会打印当前时间和字符串 “Foo”。
bar 函数是一个无限循环的异步函数,每秒钟打印当前时间和字符串 “Bar”,然后等待 1 秒钟。这里使用了 asyncio.sleep() 函数来等待指定的时间。
在异步编程中,可以使用协程来实现非阻塞的异步操作。协程是一种轻量级的线程,可以在单个线程中运行多个协程,从而实现并发操作。在这个示例中,使用了 asyncio 库提供的协程机制来实现异步操作。使用 asyncio.run() 函数来运行这个示例,它会自动创建一个事件循环,并将 foo 和 bar 函数注册到事件循环中。
main.py
import asyncio
from controllers import foo, bar
from apscheduler.schedulers.asyncio import AsyncIOScheduler
async def main():
# Init message
print('\nPress Ctrl-C to quit at anytime!\n')
scheduler = AsyncIOScheduler()
scheduler.add_job(foo, "interval", seconds=2)
scheduler.start()
await asyncio.create_task(bar())
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.create_task(main())
loop.run_forever()
代码说明,
这段代码使用了 asyncio 库和 apscheduler 库来实现一个定时任务调度器。具体来说,它定义了一个名为 main 的异步函数,它包含了以下步骤:
- 打印一条初始化消息,告诉用户如何退出程序。
- 创建一个 AsyncIOScheduler 对象,用于调度定时任务。
- 向调度器中添加一个名为 foo 的任务,每 2 秒钟运行一次。
- 启动调度器。
- 创建一个协程任务,调用名为 bar 的异步函数。
- 运行事件循环,等待协程任务完成。
如果运行的是这个文件,那么在最后一行的 if __name__ == "__main__"
分支中,会创建一个事件循环,并将 main 函数作为一个协程任务添加到事件循环中。然后,调用 loop.run_forever() 方法来运行事件循环,等待协程任务完成。这样,就可以实现定时任务调度器的功能。
运行效果,
Press Ctrl-C to quit at anytime!
2023-05-31 14:47:30.461780 Bar
2023-05-31 14:47:31.468809 Bar
2023-05-31 14:47:32.471349 Bar
2023-05-31 14:47:32.472350 Foo
2023-05-31 14:47:33.484869 Bar
2023-05-31 14:47:34.463051 Bar
2023-05-31 14:47:34.463051 Foo
2023-05-31 14:47:35.478448 Bar
2023-05-31 14:47:36.475942 Bar
2023-05-31 14:47:36.475942 Foo
2023-05-31 14:47:37.487639 Bar
2023-05-31 14:47:38.484124 Bar
...
2. 示例代码 2
basic_generation.py
import asyncio
import edge_tts
import pygame
# TEXT = "Hello World! Note that this code requires the requests and pygame modules to be installed. "
TEXT = "想了解一下 Python 的异步编程,所以就学习了一些示例代码。"
VOICE = "zh-CN-XiaoxiaoNeural"
OUTPUT_FILE = "./output/output.mp3"
async def _main() -> None:
communicate = edge_tts.Communicate(TEXT, VOICE)
await communicate.save(OUTPUT_FILE)
pygame.mixer.init()
pygame.mixer.music.load("./output/output.mp3")
pygame.mixer.music.play()
# Wait for audio to finish playing
while pygame.mixer.music.get_busy():
continue
if __name__ == "__main__":
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(_main())
finally:
loop.close()
代码说明,
这段代码使用了 Python 的异步编程模块 asyncio 和 Edge TTS API,实现了将指定的文本转换为语音并播放的功能。具体来说,代码的主要功能如下:
- 导入必要的模块和变量,包括 asyncio、edge_tts 和 pygame。
- 定义要转换为语音的文本(TEXT)、所使用的语音合成引擎(VOICE)和输出文件路径3.(OUTPUT_FILE)。
- 定义一个异步函数 _main(),该函数使用 Edge TTS API 将文本转换为语音,并使用 pygame 播放输出的音频文件。
- 在主函数中,获取异步事件循环,并运行 _main() 函数直到完成。
- 最后,关闭异步事件循环。
具体实现过程如下:
- 使用 edge_tts.Communicate() 函数创建一个 Communicate 对象,该对象包含了要转换为语音的文本和所使用的语音合成引擎。
- 使用 await communicate.save(OUTPUT_FILE) 异步保存输出的音频文件到指定路径。
- 初始化 pygame 模块,使用 pygame.mixer.music.load() 函数加载输出的音频文件。
- 使用 pygame.mixer.music.play() 函数播放加载的音频文件。
- 在 while 循环中,使用 pygame.mixer.music.get_busy() 函数检查音频是否正在播放,如果正在播放则继续循环,直到音频播放完成为止。
3. 示例代码 3
main.py
import asyncio
import threading
async def my_coroutine():
print("Start coroutine...")
await asyncio.sleep(3)
print("Coroutine finished.")
def run_in_thread(loop):
asyncio.set_event_loop(loop)
loop.run_until_complete(my_coroutine())
if __name__ == "__main__":
loop = asyncio.new_event_loop()
t = threading.Thread(target=run_in_thread, args=(loop,))
t.start()
t.join()
loop.close()
代码说明,
这个示例代码创建了一个协程 my_coroutine(),它会在开始时打印一条消息,然后等待 3 秒钟,最后再打印一条消息。
接着,我们定义了一个名为 run_in_thread() 的函数,它接受一个事件循环对象 loop 作为参数,然后将这个事件循环对象设置为当前线程的默认事件循环,并运行协程 my_coroutine()。
在主程序中,我们首先创建了一个新的事件循环对象 loop,然后创建了一个新的线程 t,并将 run_in_thread() 函数作为它的目标函数。我们将事件循环对象 loop 作为参数传递给 run_in_thread() 函数,以便让它在新线程中运行协程 my_coroutine()。
最后,我们启动线程 t,等待它完成,然后关闭事件循环对象 loop。这样,我们就实现了在一个新线程中运行协程的功能。
在 Python 中,t.join() 是一个线程方法,它的作用是等待当前线程(即调用方法的线程)执行完毕。
在上面的示例代码中,我们创建了一个名为 t 的新线程,并在其中运行了协程 my_coroutine()。接着,我们调用 t.join() 方法,这会使主线程等待 t 线程执行完毕,然后再继续执行下面的代码。
如果我们不调用 t.join() 方法,那么主线程就会立即继续执行下面的代码,而不会等待 t 线程执行完毕。这可能会导致程序出现一些意外的行为,因为 t 线程可能还没有完成它的任务,而主线程已经开始执行下面的代码了。
因此,调用 t.join() 方法可以确保在主线程继续执行下面的代码之前,t 线程已经完成了它的任务。
未完待续!