asyncioとthreadingを用いた投げっぱなし処理について
asyncioとthreadingを用いた投げっぱなし処理について下記で書いた。
今回こちらで書いた処理をtkinterで作成したGUIアプリ上で試してみたコードを下記に貼る。
やはり実際にデスクトップアプリを書いていると投げっぱなし(fire and forget)の処理というのは結構必要になる場面が多い。
tkinterでのasyncioとthreadingを用いたサンプル
import asyncio import threading import tkinter as tk import tkinter.messagebox as tkm from time import sleep class App: def __init__(self, root): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) self.root = root root.title("Countdown sample") root.geometry("300x100") self.label = tk.Label(root, text="Please press one of the buttons") self.label.pack() tk.Button(root, text="Countdown start: 3", command=lambda:self.thread_countdown(3, "Call thread_countdown")).pack() tk.Button(root, text="Countdown start: 5", command=lambda:loop.run_in_executor(None, self.countdown, 5, "Call countdown")).pack() def countdown(self, count, msg): countdown_text = "Countdown: " for i in range(count, 0, -1): self.label["text"] = countdown_text + str(i) sleep(1) self.label["text"] = countdown_text + "0" sleep(1) tkm.showinfo("FINISH", msg) def thread_countdown(self, count, msg): thread = threading.Thread(target=self.countdown, args=(count, msg)) thread.start() def main(): root = tk.Tk() app = App(root) app.root.mainloop() if __name__ == "__main__": main()
こちらのコードを実行すると、下記のような画面が表示される。
それぞれのボタンを押すことでasyncioとthreadingそれぞれの処理を動かすことができる。
上に貼ったサンプルコードをもとに、それぞれの処理に関する詳細を書いておく。
asyncioを用いた投げっぱなし処理
def __init__(self, root): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) ・ ・ ・ tk.Button(root, text="Countdown start: 5", command=lambda:loop.run_in_executor(None, self.countdown, 5, "Call countdown")).pack()
asyncioに関する処理は一番上に貼ったポストで書いたとおりで、ここでは __init__
内でイベントループを作成している。
tkinterでボタン押下時のイベントハンドラの設定はcommand
で行うので、 command=lambda:loop.run_in_executor(None, self.countdown, 5, "Call countdown")
という箇所で投げっぱなし処理を実現している。
threadingを用いた投げっぱなし処理
変わってthreadingの方はイベントハンドラへの設定は普段の設定と変わらずに下記のように行っている。
tk.Button(root, text="Countdown start: 3", command=lambda:self.thread_countdown(3, "Call thread_countdown")).pack()
が、代わりに関数の呼び出し側でスレッドを別に作成し、そちらでスレッドをスタートさせて処理を投げっぱなしにさせている。
def countdown(self, count, msg): # カウントダウン処理 def thread_countdown(self, count, msg): thread = threading.Thread(target=self.countdown, args=(count, msg)) thread.start()
ちなみにthreading.Thread
で関数を実行する際に引数を渡す必要がある場合、別途 args
から関数を渡す必要がある。
最初普通に thread = threading.Thread(target=self.countdown(count, msg))
としてしまっていて、想定通りに動かずに10分ほどハマった。
(ちゃんとドキュメントを読めという話だった...)
なお、該当するドキュメントは下記となる。
threading --- スレッドベースの並列処理 — Python 3.10.0b2 ドキュメント
以上、tkinterとasyncioとthreadingに関する備忘録でした。
なお、コードはGitHubにも上げています。