asyncio を使用してマルチスレッド非同期マルチタスク インターフェイス操作を実装する

b4e0f5fffa7dcf7e67f7c6ccfcaed65b.gif

序文

最近、ウィンドウ クライアントから開始される非同期マルチタスク リクエストを必要とするプロジェクトがあります。長期にわたるバックグラウンド操作がフォアグラウンド操作をブロックするのを防ぐために、マルチスレッドを開始して 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
162dd50a9996c146ca9b33133844684a.png

マルチタスクコルーチン

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)
3e23eee0462cf73c2bf2f551407c0a3b.png

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
694798e38abf429d5ff5dbb765816de4.png

プロセスを要約すると、次のようになります。

  • 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

同時マルチタスクコルーチン

サブタスク間に依存関係がない場合は、同時実行を使用してプログラムの実行効率を大幅に向上させることができます。

1d3575cf30a2a99b2ec1b2f85220de7c.png

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()
539d2ff42c6fbe9a1555ec34843bfbc1.png

参考文献

さらに詳しく読むには、基本原則から設計パターンまで非常に詳細に説明されている「Python の Async IO: A Complete Walkthrough」を参照してください。

https://realpython.com/async-io-python/

ソースコードのダウンロード

ff7f529709a6c88f89d6b33937c78ef4.pngこの問題の完全なソース コードは、公開アカウント「Deep Awakening」にあり、バックグラウンドで「asyncio」と返信すると、ダウンロード リンクが表示されます。

PS: 深夜にコーディングする場合、コーダーは本質的にコーヒーをコードに変換する生き物です。しかし、私の体はコーヒーの飲みすぎに耐えられないので、最近はコーヒーをやめて、グレープフルーツティーに夢中になっています。

恒寿堂のハニーグレープフルーツティーがおすすめです。1個買うと1個無料です。韓国のものより美味しいです。ちなみに私は国産品を応援しています。

7f464902a2b5b5d5406f0ed2dcf2fb8c.gif

おすすめ

転載: blog.csdn.net/weixin_47479625/article/details/120793384