Operation KiWi

一生使える言語はPythonだと信じてる

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にやらせよう ―ノンプログラマーにもできる自動化処理プログラミング