CLOVER🍀

That was when it all began.

Pythonのスレッドは、ネイティブスレッドなのか?

Pythonでのスレッドといえば、GIL(Global Interpreter Lock)があるので同時にはひとつのスレッドしか実行できない
という話らしいです。

スレッド状態 (thread state) とグローバルインタプリタロック (global interpreter lock)

動的言語総まとめ : PythonのGILが徹底改善されるも廃止はされない。SqueakがAndroidに移植された。

Pythonのマルチスレッドで気を付けるべきこと | 凡人のブログ

ところで、このスレッドってOSのネイティブスレッドなのでしょうか?ちょっと確認してみたくなりました。

環境

確認した環境は、こちら。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.2 LTS
Release:    18.04
Codename:   bionic


$ python3 -V
Python 3.6.7

Ubuntu Linux 18.04 LTS、Python 3.6です。

確認

こんなプログラムを用意。
thread-example.py

import threading
import time

def run():
    current_thread_name = threading.current_thread().getName()
    print("start Thread = %s" % current_thread_name)

    time.sleep(10)

    print("end Thread = %s" % current_thread_name)

print("start!!")

threads = [threading.Thread(target = run, name = "Thread-%d" % (i + 1)) for i in range(5)]

for thread in threads:
    thread.start()

for thread in threads:
    thread.join()

print("finish!!")

スレッドを5つ用意して開始、各スレッドは10秒待機して終了します。

まずは、実行。

$ time python3 thread-example.py 
start!!
start Thread = Thread-1
start Thread = Thread-2
start Thread = Thread-4
start Thread = Thread-3
start Thread = Thread-5
end Thread = Thread-4
end Thread = Thread-3
end Thread = Thread-2
end Thread = Thread-1
end Thread = Thread-5
finish!!

real    0m10.039s
user    0m0.030s
sys 0m0.000s

10秒で終わりました。

では、このプログラムを実行中に

$ time python3 thread-example.py

もうひとつのターミナルで、プロセスから派生したスレッドを確認してみます。

$ ps aux -L | grep python3 | grep thread-example.py
xxxxx 13748 13748  0.1    6  0.0 407140  9912 pts/0    Sl+  00:24   0:00 python3 thread-example.py
xxxxx 13748 13749  0.0    6  0.0 407140  9912 pts/0    Sl+  00:24   0:00 python3 thread-example.py
xxxxx 13748 13750  0.0    6  0.0 407140  9912 pts/0    Sl+  00:24   0:00 python3 thread-example.py
xxxxx 13748 13751  0.0    6  0.0 407140  9912 pts/0    Sl+  00:24   0:00 python3 thread-example.py
xxxxx 13748 13752  0.0    6  0.0 407140  9912 pts/0    Sl+  00:24   0:00 python3 thread-example.py
xxxxx 13748 13753  0.0    6  0.0 407140  9912 pts/0    Sl+  00:24   0:00 python3 thread-example.py

全部で6つありますね。ひとつ多いのは、メインスレッドですね。というわけで、OSネイティブなスレッドのようです。

ソースコードをさらっと見てみましょう。

https://github.com/python/cpython/blob/v3.6.7/Lib/threading.py#L32

https://github.com/python/cpython/blob/v3.6.7/Python/thread.c#L84-L87

https://github.com/python/cpython/blob/v3.6.7/Python/thread_pthread.h#L159-L178

POSIXスレッドみたいですね。

Windowsの場合は…?

https://github.com/python/cpython/blob/v3.6.7/Python/thread.c#L89-L92

https://github.com/python/cpython/blob/v3.6.7/Python/thread_nt.h#L145-L151

ところで、「GILがある割にはスリープするプログラムは10秒で終わっているのでは?」と思うかもなのですが、
これはスリープ時にGILを解放するからでしょうねぇ…。

karky7のブログ: PythonのGILについて簡単に調べてみました

ちらっとソースコードを見てみると、そうみたいですね。

https://github.com/python/cpython/blob/v3.6.7/Modules/timemodule.c#L1447-L1449

Py_BEGIN_ALLOW_THREADS
... Do some blocking I/O operation ...
Py_END_ALLOW_THREADS

拡張コード内で GIL を解放する