M-Y-Y
@M-Y-Y

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

ThreadPoolExecutorでメインスレッドと並行処理したい

Tkinterで、APIの戻り待ちをしつつ画面にシークバー的なものを表示しようとしています。
画面更新はメインスレッドで行う必要があるようなので↓のように書けばメインスレッドと並行して処理が行えると思ったのですが、
実際は heavy_task() → (stats_finish()) → update_stats() と順番に処理されています。

明示的にメインスレッドを待たなくする方法、もしくは別スレッドでTkinterの画面を更新する方法、もしくはそもそももっとスマートに実装する方法等はあるでしょうか…?


# 処理中…をぐるぐる回す
def update_stats(root):
    while fin:
        stats['text'] =  status_icons[count]
        root.update()
        time.sleep(1)

        count = count+1
        if count > 3:count = 0


with ThreadPoolExecutor(max_workers=2, thread_name_prefix="thread") as executor:
    future = executor.submit(heavy_task)

    #  処理が終わったらupdate_statsにお知らせする用
    future.add_done_callback(stats_finish)  

    
update_stats(root) #  これがheavy_taskと平行してほしい

result = future.result() #  ここで待ち合わせしてほしい
0

1Answer

ThreadPoolExecutor を使った場合、メインスレッドでスレッド管理が動き続けるので、tk.mainloop() との並列実行ができないと思います。
threadingでスレッド起動してはいかがでしょうか?

import time
import threading
import tkinter as tk

root = tk.Tk()
root.geometry('100x100')
status = tk.StringVar()
tk.Label(textvariable=status).pack()

running = True
status_icons = '|', '/', '-', '\\'

# 処理中…をぐるぐる回す
def update_status():
    print("status start")
    count = 0
    while running:
        status.set(status_icons[count])
        time.sleep(1)
        count = (count + 1) % len(status_icons)
    print("status end")


def status_finish():
    global running
    running = False
    status.set("Finish!")


def heavy_task():
    print("heavy start")
    time.sleep(10)
    print("heavy end")
    status_finish()


status_thread = threading.Thread(target=update_status)
status_thread.start()
heavy_thread = threading.Thread(target=heavy_task)
heavy_thread.start()

tk.mainloop()
1Like

Comments

  1. @M-Y-Y

    Questioner

    Python3以上ならconcurrent.futuresが推奨です!というのを鵜呑みにしていましたがこのようなときはthreadingを使うべきなんですね…。
    ありがとうございます!サンプルコードを参考にさせていただき書き換えます。

Your answer might help someone💌