序文
最近、ウィンドウ クライアントから開始される非同期マルチタスク リクエストを必要とするプロジェクトがあります。長期にわたるバックグラウンド操作がフォアグラウンド操作をブロックするのを防ぐために、マルチスレッドを開始して UI メイン インターフェイスのイベント ループを分離する必要があり、 asyncio バックグラウンド非同期タスク イベント ループ。
サポートライブラリの紹介
1. 非同期コルーチン ライブラリ asyncio
asyncio
コルーチンを使用してイベント ループを実装し、非同期タスクの効果を実現します。
pip install asyncio
コルーチンは@asyncio.coroutine
デコレータを使用して非同期タスクをマークし、yield from
非同期タスクが完了するのを待ちます。で
@asyncio.coroutine === 非同期
=== からの降伏を待つ
キーワードが導入されて以来async/await
、コードの実装はよりエレガントになりました。
2. インターフェースライブラリ wxPython
wxPython は、 Python プログラミング言語のクロスプラットフォームGUI ツールキットです。これにより、Python プログラマーは強力で強力なグラフィカル ユーザー インターフェイスを備えたプログラムを簡単かつ簡単に作成できます。これは、C++ で書かれた人気のある wxWidgets クロスプラットフォーム ライブラリの GUI コンポーネントをラップする Python 拡張モジュールのセットとして実装されます。
pip install wxpython
特にwindows
次の例、多くのユニークでクールなウィンドウ効果を実現できます。詳細については、wxDemo
サンプルを実行して参照してください。
# 运行 python目录下/Scripts/wxdemo.exe
C:\ProgramData\Anaconda3\envs\ctrip_code36\Scripts>wxdemo.exe
マルチタスクコルーチン
1. 非同期タスクを作成する
まず単純に非同期タスクを作成し、タスク期間をランダム化し、タスクの開始時刻と終了時刻を記録します。
async def AsyncTask(self, name):
t = random.randint(1, 5)
self.log('Start %s: %.8f, duration %ds' % (name, time.time() - self.start, t))
await asyncio.sleep(t) # 模拟长时间后台操作
self.log('End %s: %.8f' % (name, time.time() - self.start))
return name
async
キーワードを使用して非同期関数を定義し、await
操作が戻るまで待ちます。asyncio.sleep()
実際の操作をシミュレートするためにここで使用されますIO
。
非同期タスクの終了後にトリガーされるコールバック関数を設定します。
def task_callback(self, task):
time.sleep(1)
self.log('Callback %s: %.8f' % (task.result(), time.time() - self.start))
2. ビルドウィンドウのインターフェース
非同期タスクをトリガーするボタンと、現在のタスクのステータスを表示する静的テキストを含む単純なウィンドウを描画します。
class MyFrame(wx.Frame):
def __init__(
self, parent, ID, title, pos=wx.DefaultPosition,
size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE
):
wx.Frame.__init__(self, parent, ID, title, pos, size, style)
panel = wx.Panel(self, -1)
self.start = time.time() # 记录初始时间戳
btn = wx.Button(panel, -1, "启动异步任务", size=(100, 30))
btn.SetPosition((20, 20))
txt = wx.StaticText(panel, -1, "任务未运行", (200, 40))
txt.SetPosition((30, 70))
self.txt = txt
self.Bind(wx.EVT_BUTTON, self.OnButton, btn)
self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
3. スレッドを作成する
イベント ループを取得しasyncio
、独立したスレッドを開始し、メイン インターフェイス イベントを分離します。
def OnButton(self, evt):
async_loop = asyncio.get_event_loop()
if not async_loop.is_running():
threading.Thread(target=self._asyncio_thread, args=(async_loop,)).start()
return
4. メインタスクを作成する
task1
独立したサブタスクと一連の同時サブタスクをtask2
含むメイン タスクを設定しますtask3
。
まずメインタスクが
Master task
開始されます。5 秒間のサブタスクが開始されます
task1
。task2
完了後、5 秒のサブタスクと 4 秒のサブタスクが同時に開始されtask3
、コールバック関数が設定されました。この場合、
task3
タスク時間が短く、タスクが最初に完了し、タスクのコールバックがトリガーされます。その後、
task2
それが完了し、対応するコールバックが実行されます。最後に
Master task
終了します。
def _asyncio_thread(self, async_loop):
self.log('Start %s: %.8f' % ('Master task', time.time() - self.start))
asyncio.set_event_loop(async_loop)
print('异步单任务', 'task1')
task = asyncio.ensure_future(self.AsyncTask('task1'))
async_loop.run_until_complete(task)
print('异步多任务并发', 'task2', 'task3')
tasks = []
task = asyncio.ensure_future(self.AsyncTask('task2'))
task.add_done_callback(self.task_callback)
tasks.append(task) # 将多个任务对象装在到一个任务列表中
task = asyncio.ensure_future(self.AsyncTask('task3'))
task.add_done_callback(self.task_callback)
tasks.append(task)
async_loop.run_until_complete(asyncio.wait(tasks))
self.log('End %s: %.8f' % ('Master task', time.time() - self.start))
return
プロセスを要約すると、次のようになります。
asyncio
イベントループを取得しますasync_loop = asyncio.get_event_loop()
。スレッドを起動し、
async_loop
それを子スレッドに挿入します。サブスレッドにイベント ループを設定し、メイン インターフェイスのイベント ループを分離します。
asyncio.set_event_loop(async_loop);
非同期タスクを構成するために使用します
asyncio.ensure_future
。set コールバック関数を使用します
add_done_callback
。非同期関数を実行するために使用します
async_loop.run_until_complete
。
同時マルチタスクコルーチン
サブタスク間に依存関係がない場合は、同時実行を使用してプログラムの実行効率を大幅に向上させることができます。
1. 複数の非同期タスクを作成する
def OnButton(self, evt):
async_loop = asyncio.get_event_loop()
if not async_loop.is_running():
t = threading.Thread(target=self._asyncio_thread, args=(async_loop,))
t.start()
for i in range(5):
asyncio.run_coroutine_threadsafe(self.AsyncTask('task%d' % i), async_loop)
2. スレッドを作成する
def _asyncio_thread(self, async_loop):
asyncio.set_event_loop(async_loop)
async_loop.run_forever()
3. イベントループを終了する
async_loop
スレッド内のスレッドが開始されるためrun_forever()
、プログラムを閉じるときに、call_soon_threadsafe(async_loop.stop)
プログラムがスムーズに終了するように、対応するクリーニング操作を実行する必要があることに注意してください。
def OnCloseWindow(self, evt):
async_loop = asyncio.get_event_loop()
async_loop.call_soon_threadsafe(async_loop.stop)
self.Destroy()
参考文献
さらに詳しく読むには、基本原則から設計パターンまで非常に詳細に説明されている「Python の Async IO: A Complete Walkthrough」を参照してください。
https://realpython.com/async-io-python/
ソースコードのダウンロード
この問題の完全なソース コードは、公開アカウント「Deep Awakening」にあり、バックグラウンドで「asyncio」と返信すると、ダウンロード リンクが表示されます。
PS: 深夜にコーディングする場合、コーダーは本質的にコーヒーをコードに変換する生き物です。しかし、私の体はコーヒーの飲みすぎに耐えられないので、最近はコーヒーをやめて、グレープフルーツティーに夢中になっています。
恒寿堂のハニーグレープフルーツティーがおすすめです。1個買うと1個無料です。韓国のものより美味しいです。ちなみに私は国産品を応援しています。