Python のスレッドを理解する

巴扎黑
リリース: 2017-04-30 14:38:28
オリジナル
1239 人が閲覧しました

Python でのスレッドの使用例と、スレッドの競合を回避する方法をいくつか見ていきます。

次の例を複数回実行して、スレッドが予測不可能であること、およびスレッドが実行されるたびに異なる結果を生成することを確認できるようにする必要があります。免責事項: GIL についてこれ以降聞いたことは忘れてください。GIL は私が示そうとしているものには影響しません。

例 1

5 つの異なる URL をリクエストします:

シングルスレッド

import time
import urllib2

def get_responses():
    urls = [
        'http://www.google.com',
        'http://www.amazon.com',
        'http://www.ebay.com',
        'http://www.alibaba.com',
        'http://www.reddit.com'
    ]
    start = time.time()
    for url in urls:
        print url
        resp = urllib2.urlopen(url)
        print resp.getcode()
    print "Elapsed time: %s" % (time.time()-start)

get_responses()
ログイン後にコピー

出力は次のとおりです:

http://www.google.com 200
http://www.amazon.com 200
http://www.ebay.com 200
http://www.alibaba.com 200
http://www.reddit.com 200
Elapsed time: 3.0814409256
ログイン後にコピー

説明:

  • URLは順番にリクエストされます


  • CPU は、1 つの URL から応答を取得しない限り、次の URL をリクエストしません


  • ネットワーク リクエストには時間がかかるため、CPU はネットワーク リクエストが返されるまでアイドル状態になります。

マルチスレッド

import urllib2
import time
from threading import Thread

class GetUrlThread(Thread):
    def __init__(self, url):
        self.url = url 
        super(GetUrlThread, self).__init__()

    def run(self):
        resp = urllib2.urlopen(self.url)
        print self.url, resp.getcode()

def get_responses():
    urls = [
        'http://www.google.com', 
        'http://www.amazon.com', 
        'http://www.ebay.com', 
        'http://www.alibaba.com', 
        'http://www.reddit.com'
    ]
    start = time.time()
    threads = []
    for url in urls:
        t = GetUrlThread(url)
        threads.append(t)
        t.start()
    for t in threads:
        t.join()
    print "Elapsed time: %s" % (time.time()-start)

get_responses()
ログイン後にコピー

出力:

http://www.reddit.com 200
http://www.google.com 200
http://www.amazon.com 200
http://www.alibaba.com 200
http://www.ebay.com 200
Elapsed time: 0.689890861511
ログイン後にコピー

説明:

  • プログラム実行時間の向上を実現


  • CPU の待ち時間を短縮するために、マルチスレッド プログラムを作成しました。あるスレッドのネットワーク リクエストが返されるのを待っているときに、CPU は他のスレッドに切り替えて、他のスレッドでネットワーク リクエストを行うことができます。


  • スレッドが URL を処理することを期待しているため、スレッド クラスをインスタンス化するときに URL を渡します。


  • スレッドの実行とは、クラス内の run() メソッドを実行することを意味します。


  • 何を望んでも、各スレッドは実行する必要があります run()


  • URL ごとにスレッドを作成し、start() メソッドを呼び出します。これにより、スレッド内で start()方法,这告诉了cpu可以执行线程中的run() メソッドを実行するように CPU に指示されます。


  • すべてのスレッドの実行が完了するまでの時間を計算したいので、join() メソッドを呼び出します。


  • join()次の命令を実行する前に、このスレッドが終了するまで待機するようにメインスレッドに通知できます。


  • 各スレッドに対して join() メソッドを呼び出すため、すべてのスレッドの実行が完了した後に実行時間を計算します。

スレッドについて:

  • CPU は、start() を呼び出した直後に start()后马上执行run() メソッドを実行しない場合があります。


  • 異なるスレッド間の実行順序を決定することはできません。 run()


  • 単一スレッドの場合、

    メソッド内のステートメントが順番に実行されることが保証されます。 run()


  • これは、スレッド内の URL が最初に要求され、その後、返された結果が出力されるためです。

例 2

プログラムを使用して、複数のスレッド間のリソース競合を実証し、問題を解決します。

りー

このプログラムを複数回実行すると、さまざまな結果が表示されます。

説明:

  • すべてのスレッドが変更したいグローバル変数があります。


  • すべてのスレッドは、このグローバル変数に 1 を追加する必要があります。


  • スレッド数が 50 の場合、最終値は 50 になるはずですが、実際はそうではありません。

なぜ50に達しなかったのでしょうか?

  • で。 some_var15的时候,线程t1读取了some_var,这个时刻cpu将控制权给了另一个线程t2


  • t2线程读到的some_var也是15


  • t1t2都把some_var加到16


  • 当時私たちが期待していたのは

    t1 t2两个线程使some_var + 2变成17


  • ここでは資源をめぐる競争が起きています。


  • 他のスレッドでも同様の状況が発生する可能性があるため、最終的な結果は

    よりも低くなる可能性があります。 50

リソース競争を解く

from threading import Thread

#define a global variable
some_var = 0 

class IncrementThread(Thread):
    def run(self):
        #we want to read a global variable
        #and then increment it
        global some_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)

def use_increment_thread():
    threads = []
    for i in range(50):
        t = IncrementThread()
        threads.append(t)
        t.start()
    for t in threads:
        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

class CreateListThread(Thread):
    def run(self):
        self.entries = []
        for i in range(10):
            time.sleep(1)
            self.entries.append(i)
        print self.entries

def use_create_list_thread():
    for i in range(3):
        t = CreateListThread()
        t.start()

use_create_list_thread()
ログイン後にコピー

  运行几次后发现并没有打印出争取的结果。当一个线程正在打印的时候,cpu切换到了另一个线程,所以产生了不正确的结果。我们需要确保print self.entries是个逻辑上的原子操作,以防打印时被其他线程打断。

  我们使用了Lock(),来看下边的例子。

from threading import Thread, Lock
import time

lock = Lock()

class CreateListThread(Thread):
    def run(self):
        self.entries = []
        for i in range(10):
            time.sleep(1)
            self.entries.append(i)
        lock.acquire()
        print self.entries
        lock.release()

def use_create_list_thread():
    for i in range(3):
        t = CreateListThread()
        t.start()

use_create_list_thread()
ログイン後にコピー

  这次我们看到了正确的结果。证明了一个线程不可以修改其他线程内部的变量(非全局变量)。

  原文出处: Akshar Raaj

以上がPython のスレッドを理解するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート