例 1
5 つの異なる URL をリクエストします:
シングルスレッド
import time import urllib2 defget_responses(): urls=[ ‘http://www.baidu.com', ‘http://www.amazon.com', ‘http://www.ebay.com', ‘http://www.alibaba.com', ‘http://www.jb51.net' ] start=time.time() forurlinurls: printurl resp=urllib2.urlopen(url) printresp.getcode() print”Elapsed time: %s”%(time.time()-start) get_responses()
出力は次のとおりです:
http://www.baidu.com200
http://www.amazon.com200
http:// www .ebay.com200
http://www.alibaba.com200
http://www.jb51.net200
経過
time: 3.0814409256
説明:
URLは順番にリクエストされます
CPUが1つのURLから応答を取得しない限り、次のURLをリクエストしません
ネットワークリクエストには長い時間がかかるため、CPUは戻り時間を待っていますのネットワーク要求はアイドル状態です。
マルチスレッド
import urllib2 import time from threading import Thread classGetUrlThread(Thread): def__init__(self, url): self.url=url super(GetUrlThread,self).__init__() defrun(self): resp=urllib2.urlopen(self.url) printself.url, resp.getcode() defget_responses(): urls=[ ‘http://www.baidu.com', ‘http://www.amazon.com', ‘http://www.ebay.com', ‘http://www.alibaba.com', ‘http://www.jb51.net' ] start=time.time() threads=[] forurlinurls: t=GetUrlThread(url) threads.append(t) t.start() fortinthreads: t.join() print”Elapsed time: %s”%(time.time()-start) get_responses()
出力:
http://www.jb51.net200
http://www.baidu.com200
http://www.amazon.com200
http://www.alibaba. com200
http://www.ebay.com200
経過
time:0.689890861511
説明:
プログラムの実行時間の向上を意識しました
スレッド内のネットワークリクエストが返されるのを待っているときに、CPUの待ち時間を短縮するためにマルチスレッドプログラムを作成しました。他のスレッドに切り替えて、他のスレッドでネットワーク要求を実行できます。
1 つのスレッドが 1 つの URL を処理することを期待しているため、スレッド クラスをインスタンス化するときに URL を渡します。
スレッドの実行とは、クラス内の run() メソッドを実行することを意味します。
何はともあれ、各スレッドで run() を実行する必要があります。
URL ごとにスレッドを作成し、start() メソッドを呼び出します。これにより、スレッド内で run() メソッドを実行するように CPU に指示されます。
すべてのスレッドの実行が完了するまでの時間を計算したいので、join() メソッドを呼び出します。
join() は、次の命令を実行する前に、このスレッドが終了するまで待機するようにメインスレッドに通知できます。
各スレッドで join() メソッドを呼び出したので、すべてのスレッドが実行を完了した後の実行時間を計算しました。
スレッドについて:
cpu は、start() を呼び出した直後に run() メソッドを実行しない場合があります。
異なるスレッド間での run() の実行順序を決定することはできません。
単一スレッドの場合、run() メソッド内のステートメントが順番に実行されることが保証されます。
これは、スレッド内の URL が最初にリクエストされ、その後返された結果が出力されるためです。
例 2
プログラムを使用してマルチスレッド間のリソース競合を実証し、この問題を解決します。
from threading import Thread #define a global variable some_var=0 classIncrementThread(Thread): defrun(self): #we want to read a global variable #and then increment it globalsome_var read_value=some_var print”some_var in %s is %d”%(self.name, read_value) some_var=read_value+1 print”some_var in %s after increment is %d”%(self.name, some_var) defuse_increment_thread(): threads=[] foriinrange(50): t=IncrementThread() threads.append(t) t.start() fortinthreads: t.join() print”After 50 modifications, some_var should have become 50″ print”After 50 modifications, some_var is %d”%(some_var,) use_increment_thread()
このプログラムを複数回実行すると、さまざまな異なる結果が表示されます。
説明:
グローバル変数があり、すべてのスレッドがそれを変更しようとしています。
すべてのスレッドはこのグローバル変数を追加する必要があります
1
。
スレッド数が 50 の場合、最終値は 50 になるはずですが、実際はそうではありません。
なぜ50に達しなかったのですか?
some_var が 15 の場合、スレッド t1 は some_var を読み取ります。このとき、CPU は別のスレッド t2 に制御を渡します。
t2 スレッドによって読み取られた some_var も 15 です
t1 と t2 の両方で some_var が 16 に増加します
その時点で期待していたのは t1 でした
t2 2 つのスレッドは some_var + を作成します
2 は 17 になります
ここでリソースの争奪戦が発生します。
他のスレッドでも同様の状況が発生する可能性があるため、最終結果は50未満になる可能性があります。
リソース競合の解決
from threading import Lock, Thread lock=Lock() some_var=0 classIncrementThread(Thread): defrun(self): #we want to read a global variable #and then increment it globalsome_var lock.acquire() read_value=some_var print”some_var in %s is %d”%(self.name, read_value) some_var=read_value+1 print”some_var in %s after increment is %d”%(self.name, some_var) lock.release() defuse_increment_thread(): threads=[] foriinrange(50): t=IncrementThread() threads.append(t) t.start() fortinthreads: t.join() print”After 50 modifications, some_var should have become 50″ print”After 50 modifications, some_var is %d”%(some_var,) use_increment_thread()
このプログラムを再度実行すると、期待した結果が得られました。
説明:
ロック
競合状態を防ぐために使用されます
スレッド t1 が一部の操作を実行する前にロックを取得した場合。 t1 がロックを解放する前に、他のスレッドは同じ操作を実行しません
確認したいのは、スレッド t1 が some_var を読み取ると、t1 が some_var の変更を完了するまで他のスレッドは some_var を読み取ることができないということです
このように読んでください。そして、some_var の変更は論理的になりますアトミック操作。
例 3
例を使用して、あるスレッドが他のスレッドの変数 (非グローバル変数) に影響を与えることができないことを証明してみましょう。
time.sleep() はスレッドを一時停止し、スレッドの切り替えを強制的に実行できます。
from threading import Thread import time classCreateListThread(Thread): defrun(self): self.entries=[] foriinrange(10): time.sleep(1) self.entries.append(i) printself.entries defuse_create_list_thread(): foriinrange(3): t=CreateListThread() t.start() use_create_list_thread()
何度か実行した後、期待していた結果が出力されないことがわかりました。 1 つのスレッドが印刷中に CPU が別のスレッドに切り替わるため、誤った結果が生成されます。必ず印刷する必要があります
self.entries は、他のスレッドによる印刷の中断を防ぐための論理アトミック操作です。
Lock() を使用しました。以下の例を見てください。
from threading import Thread, Lock import time lock=Lock() classCreateListThread(Thread): defrun(self): self.entries=[] foriinrange(10): time.sleep(1) self.entries.append(i) lock.acquire() printself.entries lock.release() defuse_create_list_thread(): foriinrange(3): t=CreateListThread() t.start() use_create_list_thread()
今度は正しい結果が見られました。これは、あるスレッドが他のスレッドの内部変数 (非グローバル変数) を変更できないことを証明しています。
上記は Python のマルチスレッドです。その他の関連記事については、PHP 中国語 Web サイト (www.php.cn) に注目してください。