python3.6.1のthreadingでハマったけど何とか解決した

Pythonのスレッド処理は意味がない…そんなことはありません!I/Oインバウンドならばっちり解消できます!

CPUインバウンドとI/Oインバウンド

CPUインバウンド

  • CPU待ち。数値処理などの最中に起こる。

    I/Oインバウンド

  • I/O待ち。こっちのほうがよく起こる。なにせCPUはメモリの数百倍高速であり、ディスク、ネットワークの数千、数万倍高速なのである…

実際にやってみよ~!!

以下のunko()関数は、I/Oインバウンドを疑似的に再現しています。このプログラムを普通に実装すると、最低でも、5n秒(n=ループの回数=25秒)以上はかかります。それがスレッドを利用すると、並列に作業してくれるのですっごい早い(小学生並の感想)のです。実際にやってみましょう。

まずは普通に実行してみるぞ(メインスレッドのみ)


import time
def unko(number):
print(str(number) + "本目のうんこ")
time.sleep(5)
print(str(number) + "本目のうんこぶりぶり")
start = time.time()
for i in range(5):
unko(i + 1)
print("##### 経過時間 #####")
print(time.time() - start)
print("####################")

実行結果


1本目のうんこ
1本目のうんこぶりぶり
2本目のうんこ
2本目のうんこぶりぶり
3本目のうんこ
3本目のうんこぶりぶり
4本目のうんこ
4本目のうんこぶりぶり
5本目のうんこ
5本目のうんこぶりぶり
##### 経過時間 #####
25.00123381614685
####################

予想通り、25秒かかってますね。1本ずつうんこして、終わったら次のうんこをしていますね。

 ('A` )
(⊃⌒*⌒⊂) (●)(●)(●)(●)(●)

つまり、肛門は1つしかないので、仕方がなく順番に出しているのです。これがI/Oインバウンドです。

世界一頭の悪いスレッド処理


import threading
import time
def unko(number):
print(str(number) + "本目のうんこ")
time.sleep(5)
print(str(number) + "本目のうんこぶりぶり")
thread_lists = []
start = time.time()
for i in range(5):
# target=に関数名を書く。argsに引数を書く。
# 普段の感覚でtarget(引数)としてはならない!!
th_x = threading.Thread(target=unko, name="うんこ", args=(i + 1,))
th_x.start()
thread_lists.append(th_x)
for i in thread_lists:
i.join()
print("##### 経過時間 #####")
print(time.time() - start)
print("####################")
>>> 実行結果
1本目のうんこ
2本目のうんこ
3本目のうんこ
4本目のうんこ
5本目のうんこ
1本目のうんこぶりぶり
3本目のうんこぶりぶり
2本目のうんこぶりぶり
5本目のうんこぶりぶり
4本目のうんこぶりぶり
##### 経過時間 #####
5.002015829086304
####################
なんと5秒!!!並列ってすごい!!!

よく見ると、2本目を出し終わる前に3本目を出し終わるという荒業…このことからもThreadingは順番ではなく処理が終わった順ということが分かります。

さて、以上の結果から分かるように、スレッド処理は、疑似的に肛門を増やすことができます。

 ('A` )
(⊃⌒*⌒⊂) (●)
(⊃⌒*⌒⊂) (●)
(⊃⌒*⌒⊂) (●)
(⊃⌒*⌒⊂) (●)
(⊃⌒*⌒⊂) (●)

ただし、Pythonの場合は肛門(I/O)は増やせても頭(CPU)はそのままなのです。これはGIL(グローバルインタープリタロック)というPython実装上の仕様なので我々一般ピーポーには如何様にもし難いのです。だがしかし、スレッドが無意味なんてことは全くない事が分かりますね!パチパチ!!

それにしても、この程度なら楽勝で同時にこなせちゃうんですね。PCにとってはI/O待ちが圧倒的にストレスな理由がわかります。例えるなら、大学病院の待合室ですね。あれはとんでもないI/Oインバウンドですねww

その点CPUってすごい..intel入ってる

まとめ

  • Threadingは凄い
  • PythonはGIL(グローバルインタープリタロック)を利用しているため、スレッドを利用してもCPUインバウンドは解消されない
  • I/Oインバウンドが発生する処理に使おう
    • 私はスクレイピングに活用してみましたが、早くなりすぎてログが一瞬で流れてしまいましたw

終わり

この記事は、入門Python 3を見ながらやりました。この本がなかったらPythonなんて使えなかったでしょう…w

今流行りの(?)asyncも使ってみたいですね~。少しやってみたんですが、全然わからないので再度挑戦したいと思います…

退屈なことはPythonにやらせよう ―ノンプログラマーにもできる自動化処理プログラミング

退屈なことはPythonにやらせよう ―ノンプログラマーにもできる自動化処理プログラミング

コメントを残す

メールアドレスが公開されることはありません。