Pythonのスレッディングについて理解しようとしています。ドキュメントやサンプルを見ましたが、率直に言って、多くのサンプルは洗練されすぎていて、理解するのに苦労しています。
マルチスレッドのためにタスクが分割されていることを明確に示すにはどうしたらよいでしょうか?
ここでは簡単な例として、いくつかの代替URLを試して、最初に反応したURLの内容を返す必要があります。
import Queue
import threading
import urllib2
# called by each thread
def get_url(q, url):
q.put(urllib2.urlopen(url).read())
theurls = ["http://google.com", "http://yahoo.com"]
q = Queue.Queue()
for u in theurls:
t = threading.Thread(target=get_url, args = (q,u))
t.daemon = True
t.start()
s = q.get()
print s
これは、単純な最適化としてスレッドを使用するケースです。各サブスレッドは URL が解決して応答するのを待ち、その内容をキューに入れます。各スレッドはデーモンです(メインスレッドが終了してもプロセスを維持することはありません--これはよくあることです)。メインスレッドはすべてのサブスレッドを起動し、キューに get
を実行して、いずれかのサブスレッドが put
を実行するまで待ちます。
Pythonにおけるスレッドの適切な使用法は、必ずI/O操作に関連しています(CPythonはCPU負荷の高いタスクを実行するためにマルチコアを使用しないので、スレッドを使用する唯一の理由は、I/Oの待ち時間にプロセスをブロックしないことです)。 また、キューは本質的にスレッドセーフなので、ロック、条件、イベント、セマフォ、その他のスレッド間の調整や通信の概念を気にする必要がありません。
NOTE:Python で実際に並列化するには、multiprocessing モジュールを使用して、並列に実行される複数のプロセスをフォークする必要があります (グローバルインタプリタロックのため、Python のスレッドはインターリーブを提供しますが、実際には並列ではなくシリアルに実行され、I/O 操作をインターリーブする場合にのみ役立ちます)。
しかし、単にインターリーブを探している(あるいはグローバルインタプリタロックにもかかわらず並列化できるI/O操作を行っている)のであれば、threadingモジュールがスタート地点となります。本当に簡単な例として、大きな範囲の和を、サブレンジの和を並列にとるという問題を考えてみましょう。
import threading
class SummingThread(threading.Thread):
def __init__(self,low,high):
super(SummingThread, self).__init__()
self.low=low
self.high=high
self.total=0
def run(self):
for i in range(self.low,self.high):
self.total+=i
thread1 = SummingThread(0,500000)
thread2 = SummingThread(500000,1000000)
thread1.start() # This actually causes the thread to run
thread2.start()
thread1.join() # This waits until the thread has completed
thread2.join()
# At this point, both threads have completed
result = thread1.total + thread2.total
print result
上記は非常に馬鹿げた例であることに注意してください。なぜなら、I/Oを全く行わず、CPythonではグローバルなインタプリタロックのために、インターリーブとはいえシリアルに実行されるからです(コンテキストスイッチのオーバーヘッドが追加されます)。
他の人が言っているように、CPythonはGILのためにI\Oの待ち時間にのみスレッドを使うことができます。 CPUに依存するタスクでマルチコアの恩恵を受けたい場合は、multiprocessingを使用してください。
from multiprocessing import Process
def f(name):
print 'hello', name
if __name__ == '__main__':
p = Process(target=f, args=('bob',))
p.start()
p.join()