誰がthreadingで作ったスレッドの後始末をしているのか
メインスレッドが終了すると、_threadモジュールで作ったスレッドは終了してしまう。
#! python3.3 import _thread import time def func(): ident = _thread.get_ident() print('thread {} starts'.format(ident)) time.sleep(5) print('thread {} ends'.format(ident)) print('main thread starts') _thread.start_new_thread(func, ()) time.sleep(3) print('main thread ends') """ 出力: main thread starts thread 1756 starts main thread ends """
一方、threadingモジュールで作ったスレッドは終わるまで自動で待ってくれる。
#! python3.3 import threading import time def func(): ident = threading.get_ident() print('thread {} starts'.format(ident)) time.sleep(5) print('thread {} ends'.format(ident)) print('main thread starts') threading.Thread(target=func).start() time.sleep(3) print('main thread ends') """ 出力: main thread starts thread 3612 starts main thread ends thread 3612 ends """
この待つ処理(join)は誰がやっているんだろうと思って、threading.pyの中身を見てみると、_MainThreadクラスの_exitfuncメソッドが実際にjoin()してた。しかし、この_exitfuncというメソッドは_shutdownという別名がついているだけで、誰が呼んでいるのか不明なんだ。__del__もないし、atexitも呼んでいない。どうやってPython終了時に実行してるんだろう?
そこで検索してみたら http://sh1.2-d.jp/b/2008-01-10-02-17.html に書いてあった。現在のPythonでは少し仕組みが変わっているが、呼び出し順はこう。
Modules/main.cのPy_Main ↓ Python/pythonrun.cのPy_Finalize ↓ 同ファイルのwait_for_thread_shutdown ↓ threading.pyの_shutdown
しかしなあ。これ実際の中身は「もしthreadingという名前のモジュールが読み込まれていたら_shutdownを実行する」という形だからな。「もしthreadingという同名のモジュールがあったら?*1」とか「高水準なモジュールの後始末をインタプリタが名指しでするの?」とか色々な疑問が頭をよぎってしまう。まぁでも、threadingのドキュメントを見る限り、Pythonのスレッドは発展途中っぽいからな。GILの件もあるし。単に一時しのぎ的な実装になっているだけなのかもしれない。
*1:実際にやってみると呼ばれる。