Pythonマルチスレッド

高洛峰
高洛峰オリジナル
2016-10-20 11:43:451219ブラウズ

まず最初に、マルチスレッドのアプリケーション シナリオについて説明します。Python が複数のタスクを処理する場合、これらのタスクは本質的に非同期であり、各トランザクションの実行順序は不確実で、ランダムで、予測できない場合があります。コンピューティング集約型のタスクは、複数のサブタスクに分割して順次実行することも、マルチスレッド方式で処理することもできます。ただし、I/O 集中型のタスクは、シングルスレッドで処理するのが困難です。マルチスレッドを使用しない場合は、1 つ以上のタイマーでのみ処理できます。

プロセスとスレッドについて話しましょう: プロセス (重量プロセスとも呼ばれます) はプログラムの実行です。centos と同様に、 ps -aux | で何かを実行すると、常にそれ自体によって生成されたプロセスが存在します。この grep プロセスでは、各プロセスは独自のアドレス空間、メモリ、データ スタック、および実行軌跡を記録するその他の補助データを持っているため、各プロセスは情報を直接共有できず、プロセス間通信 (IPC) のみを使用できます。

スレッド (軽量プロセス) とプロセスの最大の違いは、すべてのスレッドが同じプロセスで実行され、同じ動作環境を共有し、同じデータ空間を共有することです。したがって、スレッドはプロセスよりも簡単にデータを共有し、相互に通信し、トランザクションを同時に実行できます。

メモリプロセスとスレッドの関係を簡単に理解するために、CPU を引越し会社に例えると、この引越し会社は最初、使用する車 (プロセス) を 1 台しか持っていませんでした。非常に貧しいので、従業員は 1 人 (シングルスレッド)、その後、この引っ越し会社は 1 日に最大 5 軒しか移動できませんでした。その後、上司は車を買う代わりに、さらに n 人の従業員 (マルチスレッド) を雇いました。 -スレッド化されているため、各従業員は一度に 1 軒だけ移動し、その後休憩し、車を放棄し、他の従業員が次の家に移動するように手配されます。これでは実際には効率が向上するわけではありませんが、コストが増加します。 , これは、GIL (Global Interpreter Lock) グローバル インタープリタ ロックがスレッド セーフ (データを安全に読み取れることを保証する) を保証するためです。これは、CPU 上で同時に 1 つのスレッドしか実行できないためです。 Python、つまり、実行環境にデュアル CPU があり、Python 仮想マシンが 1 つの CPU のみを使用する場合でも、GIL によって CPython が物理マルチコアのパフォーマンスを利用して操作を高速化できなくなることを意味します。詳細な説明 (歴史に残された問題、ハードウェアの発展が速すぎる) については、このブログを参照してください:

http://blog.sina.com.cn/s/blog_64ecfc2f0102uzzf.html

Python コア プログラミング、著者は次の理由から、スレッド モジュールではなくスレッド モジュールを使用することを強くお勧めします:

1. メイン スレッドが終了すると、他のすべてのスレッドはクリアされずに終了します。スレッド モジュールはすべての子の安全な終了を保護できません。スレッド。つまり、スレッド モジュールはデーモンをサポートしません。

2. スレッドモジュールの属性がスレッド化と競合する可能性があります。

3. 低レベルのスレッド モジュールには同期プリミティブがほとんどありません (実際には 1 つだけあり、スリープである必要があります)。

1. Thread モジュール

以下は、GIL を使用しない場合と GIL を使用する場合の 2 つのサンプル コードです:

1. GIL を使用しない場合のコード サンプル:

from time import sleep,ctime
import thread

def loop0():
    print 'start loop 0 at: ',ctime()
    sleep(4)
    print 'loop 0 done at: ',ctime()
def loop1():
    print 'start loop 1 at: ',ctime()
    sleep(2)
    print 'loop 1 done at: ',ctime()
def main():
    print 'start at: ',ctime()
    thread.start_new_thread(loop0,())
    thread.start_new_thread(loop1,())
    sleep(6)
    print 'all loop is done, ' ,ctime()

if __name__=='__main__':
    main()
 

输出结果:

start at:  Thu Jan 28 10:46:27 2016
start loop 0 at:   Thu Jan 28 10:46:27 2016

start loop 1 at:   Thu Jan 28 10:46:27 2016
loop 1 done at:  Thu Jan 28 10:46:29 2016
loop 0 done at:  Thu Jan 28 10:46:31 2016
all loop is done,  Thu Jan 28 10:46:33 2016

上記の出力からわかるように、2 つのスレッドが正常に開始されました。 2 秒でループ 1 が最初に完了し、4 秒でループ 0 が完了し、さらに 2 秒後にメイン スレッドが完了します。メインスレッド全体が 6 秒を経過し、loop0 とloop1 が同時に完了します。

2. GIL を使用したコード例:

import thread
from time import sleep,ctime
loops = [4,2]
def loop(nloop,nsec,lock):
    print 'start loop',nloop,'at: ',ctime()
    sleep(nsec)
    print 'loop',nloop,'done at:',ctime()
    lock.release()
def main():
    print 'starting at:',ctime()
    locks = []
    nloops = range(len(loops))

    for i in nloops:
        lock = thread.allocate_lock()                          #创建锁的列表,存在locks中
        lock.acquire()                         
        locks.append(lock)                                      
    for i in nloops:
        thread.start_new_thread(loop,(i,loops[i],locks[i]))    #创建线程,参数为循环号,睡眠时间,锁
    for i in nloops:
        while locks[i].locked():                              #等待循环完成,解锁
            pass
    print 'all DONE at:',ctime()
if __name__ == '__main__':
    main()
 

以上输出如下:

starting at: Thu Jan 28 14:59:22 2016
start loop  0  at:   Thu Jan 28 14:59:22 2016

start loop  1  at:   Thu Jan 28 14:59:22 2016
loop 1 done at: Thu Jan 28 14:59:24 2016
loop 0 done at: Thu Jan 28 14:59:26 2016
all DONE at: Thu Jan 28 14:59:26 2016

は 4 秒続きます。これにより効率が向上し、メインスレッドで時間を計測するのに sleep() 関数を使用するよりも合理的です。

2. スレッドモジュール

1. Thread クラス

thread クラスでは、次の 3 つのメソッドを使用してスレッドを作成できます:

(1) スレッド インスタンスを作成し、それに関数を渡します

(2) ) スレッドインスタンスを作成し、呼び出し可能なクラスオブジェクトを渡します

(3) スレッドからサブクラスを派生し、このサブクラスのオブジェクトを作成します

方法(1)

__author__ = 'dell'
import threading
from time import sleep,ctime
def loop0():
    print 'start loop 0 at:',ctime()
    sleep(4)
    print 'loop 0 done at:',ctime()
def loop1():
    print 'start loop 1 at:',ctime()
    sleep(2)
    print 'loop 1 done at:',ctime()
def main():
    print 'starting at:',ctime()
    threads = []
    t1 = threading.Thread(target=loop0,args=())          #创建线程
    threads.append(t1)
    t2 = threading.Thread(target=loop1,args=())
    threads.append(t2)
    for t in threads:
        t.setDaemon(True)<span style="white-space:pre">    </span>      #开启守护线程(一定要在start()前调用)
        t.start()<span style="white-space:pre">        </span>      #开始线程执行
    for t in threads:<span style="white-space:pre">                    </span>
        t.join()<span style="white-space:pre">        </span>      #将程序挂起阻塞,直到线程结束,如果给出数值,则最多阻塞timeout秒

if __name__ == &#39;__main__&#39;:
    main()
    print &#39;All DONE at:&#39;,ctime()

在这里,就不用像thread模块那样要管理那么多锁(分配、获取、释放、检查等)了,同时我也减少了循环的代码,直接自己编号循环了,得到输出如下:
 

starting at: Thu Jan 28 16:38:14 2016
start loop 0 at: Thu Jan 28 16:38:14 2016
start loop 1 at: Thu Jan 28 16:38:14 2016
loop 1 done at: Thu Jan 28 16:38:16 2016
loop 0 done at: Thu Jan 28 16:38:18 2016
All DONE at: Thu Jan 28 16:38:18 2016

結果は同じですが、コードから論理的な観点から見ると、はるかに明確です。残りの2種類はここでは掲載しません。 Thread のインスタンス化と thread.start_new_thread の呼び出しの最大の違いは、新しいスレッドがすぐに実行を開始しないことです。つまり、スレッド モジュールの Thread クラスでは、インスタンス化した後、 を呼び出した後に統合されます。 start() 関数を実行します。これにより、プログラムは良好な同期特性を持ちます。

以下はシングルスレッドとマルチスレッドの比較例で、2組の演算がそれぞれ乗算と除算で完了し、マルチスレッド化による効率の向上を示しています

from time import ctime,sleep
import threading

def multi():
    num1 = 1
    print &#39;start mutiple at:&#39;,ctime()
    for i in range(1,10):
       num1 = i*num1
       sleep(0.2)
    print &#39;mutiple finished at:&#39;,ctime()
    return num1
def divide():
    num2 = 100
    print &#39;start division at:&#39;,ctime()
    for i in range(1,10):
        num2 = num2/i
        sleep(0.4)
    print &#39;division finished at:&#39;,ctime()
    return num2
def main():
    print &#39;---->single Thread&#39;
    x1 = multi()
    x2 = divide()
    print &#39;The sum is &#39;,sum([x1,x2]),&#39;\nfinished singe thread&#39;,ctime()

    print &#39;----->Multi Thread&#39;
    threads = []
    t1 = threading.Thread(target=multi,args=())
    threads.append(t1)
    t2 = threading.Thread(target=divide,args=())
    threads.append(t2)
    for t in threads:
        t.setDaemon(True)
        t.start()
    for t in threads:
        t.join()

if __name__ == &#39;__main__&#39;:
    main()

结果如下:

 

---->single Thread

start mutiple at: Thu Jan 28 21:41:18 2016

mutiple finished at: Thu Jan 28 21:41:20 2016

start division at: Thu Jan 28 21:41:20 2016

division finished at: Thu Jan 28 21:41:24 2016

The sum is  362880 

finished singe thread Thu Jan 28 21:41:24 2016

----->Multi Thread

start mutiple at: Thu Jan 28 21:41:24 2016

start division at: Thu Jan 28 21:41:24 2016

mutiple finished at: Thu Jan 28 21:41:26 2016

division finished at: Thu Jan 28 21:41:27 2016

The sum is : 362880


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。