並行プログラミングロック問題

並行プログラミングロック問題

 PythonのGIL锁(グローバルインタプリタロック)

GILは公式文書で説明ロック

'''
定义:
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple 
native threads from executing Python bytecodes at once. This lock is necessary mainly 
because CPython’s memory management is not thread-safe. (However, since the GIL 
exists, other features have grown to depend on the guarantees that it enforces.)
翻译:在cpython中,全局解释器锁(gil)是一个互斥锁,它可以防止同时执行python字节码的本机线程。这个锁主要是必要的因为cpython的内存管理不是线程安全的。(然而,自从GIL锁的存在,其他特征已经发展到取决于它所实施的保证。)

結論:CPythonのインタプリタ、マルチスレッドと同じプロセスの下でオープン、同時に実行の唯一のスレッド、マルチコアの利点を活用することができません。

まずクリアする必要があるGILPythonの機能ではない、それが導入されたPythonのパーサ(CPythonと)の実装時の概念です。CPythonのは、ほとんどの環境Pythonの実行環境下のデフォルトですので。CPythonのは、Pythonであるので、多くの人々の中で当たり前の概念がありGIL、Python言語の不備に起因します。そこでここではまず明確にする必要があります:GILは、PythonはGILに頼ることができない、Pythonの機能ではありません。

 GILロックはじめに

GILは、すべてのミューテックスの本質であるので、本質的に、ミューテックス、ミューテックスで同一であり、全てなるシリアルが同時に実行され、同じ時間内でのデータの共有を制御するためにのみタスクによって修飾することができます、したがって、データのセキュリティを確保します。

異なるデータの安全性を守るために、あなたは別のロックを追加する必要がありますのを確認するためには、このです。

最初に一つのことを決定、GILを理解するには、次の各時間のpythonプログラムを、それが別のプロセスを持つことになります。例えばのpython test.py、Pythonのaaa.py、Pythonのbbb.pyは、3つの異なるプロセスパイソンが生成されます

'''
#验证python test.py只会产生一个进程
#test.py内容
import os,time
print(os.getpid())  # 这个就是Python解释器的进程号
time.sleep(1000)
'''
python3 test.py 
#在windows下
tasklist |findstr python  
#在linux下
ps aux |grep python

メインスレッドChengkaiチーによるプロセスでのpython、メインスレッドtest.pyまたは他だけでなく等インタプリタレベルスレッドならびにガベージコレクションオンインタプリタは、要するに、すべてのスレッドは、このプロセスで実行されています内部に、は間違いありません

1 所有数据都是共享的,这其中,代码作为一种数据也是被所有线程共享的(test.py的所有代码以及Cpython解释器的所有代码)
例如:test.py定义一个函数work(代码内容如下图),在进程内所有线程都能访问到work的代码,于是我们可以开启三个线程然后target都指向该代码,能访问到意味着就是可以执行。

2.所有线程的任务,都需要将任务的代码当做参数传给解释器的代码去执行,即所有的线程要想运行自己的任务,首先需要解决的是能够访问到解释器码的代码

要約すると:

ターゲット=仕事の複数のスレッドは、プロセスが実行される場合

複数のスレッドは、最初のアクセス権を取得するために実行されるインタプリタコードにアクセスし、ターゲット・コード・インタプリタにコードを実行する
インタープリタ・コードはすべてのスレッドで共有されているので、ガベージコレクタのスレッドもインタプリタにアクセスすることができます質問につながる離れコードが実行される:同一のデータ100は、同時にスレッド1のx = 100を実行することができるガベージコレクションを回復操作100に巧妙な方法によって行われている間、この問題は、解決されていませんGIL、Pythonインタプリタに示すように、ロック処理は、同時に1つのタスクのみの実行コードを確実にするために、です。

GILロックとロック

GIL保護段階は、図2のように、独自のデータロック処理を必要とするユーザーを保護するために、データを説明することです。

GILとマルチスレッド

GILは、存在する唯一のスレッドが実行され、同じ時間に同じプロセス

これを聞いて、一部の学生はすぐに尋ねた:プロセスは、マルチコアを活用することができますが、大きなオーバーヘッド、およびPythonは小さなオーバーヘッドをマルチスレッド化が、マルチコア利点を活用することができませんでした、それは役に立たない場合パイソンではありません!そうではありません!そうではありません!

この問題を解決するために、我々はいくつかの点に同意する必要があります。

#1. cpu到底是用来做计算的,还是用来做I/O的?

#2. 多cpu,意味着可以有多个核并行完成计算,所以多核提升的是计算性能

#3. 每个cpu一旦遇到I/O阻塞,仍然需要等待,所以多核对I/O操作没什么用处

マルチコアの場合、I / O集約型の手順場合:オープンマルチプロセス、並行して実行複数のプロセスは、各プロセス内で、コピーを(ネイティブCPUコアの数に応じて、プロセスがCPUを要する)個人がいることを理解しました次に、Pythonのインタープリタコード、同じプロセス内で複数のスレッド及びインタプリタロック(GILロック)を盗もうと、同じプロセス内で複数のスレッドが同時に実行され、各スレッドの出会いI / O障害がCPUを軽減します占領当局(+スイッチの状態を保存します)。I outを実行するため、経験タイムスライスI / Oまたは部分閉塞を複数の処理を行う場合/ Oブロック時間が一定で、CPUは、次のレベル(マルチレベルフィードバックカラム)にキューに、解放しなければならない間準備状態、再びスケジューリング、そのコアの使用が固定されていないとして、再びスケジューリングされるのを待って、

I / Oの作業者に必要な原料を提供するために、ブロッキングプロセスに対応する、職場で従業員に相当し、ない場合は原料を処理する作業者は、プロセスは作業者のためのものであるこの時点で計算され、作業者のCPUに対応私たちは、原材料の到着まで待つ、停止する必要があります。

タスクの大半は、あなたが(I / O集中型)工場乾燥原料調製工程を持っている必要があります場合は、より多くの労働者を持っており、非常に有意義なことではない、プロセスの労働者、そのような材料としてそのように人を好きではありませんそれ以外の場合は住んでいますかに、

逆に、あなたは原材料が完了している植物の場合は、当然のことながら、より多くのことを労働者、より効率的です

結論:

  計算では、CPUより良い、しかし、I / O、それはより多くの無用のCPUであるため

  もちろん、プログラムを実行し、効率性の向上と、CPUは確かに(、そこには常に改善されますどのくらいの増加の大きさに関係なく)を向上させるプログラムは、実質的に純粋なまたは純粋なコンピューティングではないので、それはI / Oであります私たちは、最終的にプログラムの相対を見ることができ、計算集約型又は無用端におけるかのI / O集中、マルチスレッドのPythonさらに分析であります

#分析:
我们有四个任务需要处理,处理方式肯定是要玩出并发的效果,解决方案可以是:
方案一:开启四个进程
方案二:一个进程下,开启四个线程

#单核情况下,分析结果: 
  如果四个任务是计算密集型,没有多核来并行计算,方案一徒增了创建进程的开销,方案二胜
  如果四个任务是I/O密集型,方案一创建进程的开销大,且进程的切换速度远不如线程,方案二胜

#多核情况下,分析结果:
  如果四个任务是计算密集型,多核意味着并行计算,在python中一个进程中同一时刻只有一个线程执行用不上多核,方案一胜
  如果四个任务是I/O密集型,再多的核也解决不了I/O问题,方案二胜
 
#结论:现在的计算机基本上都是多核,python对于计算密集型的任务开多线程的效率并不能带来多大性能上的提升,甚至不如串行(没有大量切换),但是,对于IO密集型的任务效率还是有显著提升的。

マルチスレッドおよびマルチプロセス性能比較(I / O集約的計算集約)

どのような状況下で、マルチコアCPUの例であります:

# 计算密集型的情况下,开启多进程效率更高

from multiprocessing import Process
from threading import Thread
import os,time
def work():
    res=0
    for i in range(100000000):
        res*=i
    print(res)

if __name__ == '__main__':
    l=[]
    print(os.cpu_count()) #本机为4核
    start=time.time()
    for i in range(4):
        # p=Process(target=work) #耗时15.843906164169312
        p=Thread(target=work) #耗时26.057490348815918
        l.append(p)
        p.start()
    for p in l:
        p.join()
    stop=time.time()
    print('run time is %s' %(stop-start))
from multiprocessing import Process
from threading import Thread
import threading
import os,time
def work():
    time.sleep(2)
    print('===>')

if __name__ == '__main__':
    l=[]
    print(os.cpu_count()) #本机为4核
    start=time.time()
    for i in range(50):
        # p=Process(target=work) #耗时12.597720623016357,大部分时间耗费在创建进程上
        p=Thread(target=work) #耗时 2.013115167617798
        l.append(p)
        p.start()
    for p in l:
        p.join()
    stop=time.time()
    print('run time is %s' %(stop-start))

アプリケーション:

こうしたソケット、爬虫類、ウェブとして、IO集約型のためのマルチスレッド
な財務分析などの計算集約型のための複数のプロセス、

再帰的なロックとデッドロック(デッドロックを解決)

いわゆるデッドロックは:実装プロセス内の2つの以上のプロセスまたはスレッドの現象をいう、外力が存在しない場合に、お互いを待っによって引き起こされるリソースの競合の結果は、彼らがそれを促進することができません。このとき、次のようにデッドロックがあると、システムがデッドロックと呼ばれる別のプロセスを待っているの過程で常にデッドロックを生成するために、デッドロック状態またはシステムであると言います

# 这里以开启100个线程为例
from threading import Thread,Lock,RLock
import time

# mutexA=Lock()
# mutexB=Lock()
mutexB=mutexA=RLock()  # 递归锁


class Mythead(Thread):
    def run(self):
        self.f1()
        self.f2()

    def f1(self):
        mutexA.acquire()
        print('%s 抢到A锁' %self.name)
        mutexB.acquire()
        print('%s 抢到B锁' %self.name)
        mutexB.release()
        mutexA.release()

    def f2(self):
        mutexB.acquire()
        print('%s 抢到了B锁' %self.name)
        time.sleep(2)
        mutexA.acquire()
        print('%s 抢到了A锁' %self.name)
        mutexA.release()
        mutexB.release()

if __name__ == '__main__':
    for i in range(100):
        t=Mythead()
        t.start()

'''
Thread-1 拿到A锁
Thread-1 拿到B锁
Thread-1 拿到B锁
Thread-2 拿到A锁
然后就卡住,死锁了
'''

ソリューションは、再帰的なロックは、同じリソースに対する同一スレッド複数のリクエストで、Pythonのを支援するために、PythonはリエントラントロックRLOCKを提供します。

この内部RLOCKはロックとカウンタ変数を維持したリソースは多くの時間が必要になることができるように、カウンタは、回獲得数を記録します。すべてがリリースされるまでスレッドを取得し、他のスレッドがロックを再びつかむことができます。もし代わりにRLOCKロックを使用して上記の例では、デッドロックは発生しません。

mutexA=mutexB=threading.RLock() #一个线程拿到锁,counter加1,该线程内又碰到加锁的情况,则counter继续加1,这期间所有其他线程都只能等待,等待该线程释放所有锁,即counter递减到0为止

セマフォ

ミューテックスは、トイレ(コードの実行)を使用するために一人だけ、同時に、複数の人(プロセス/スレッド)、トイレ(ロック)をつかむと同様であり、信号が公衆トイレの量に似て、公衆トイレは、複数の人がピット内殺到することができますビットキー(コードを実行する複数のスレッドまたはプロセスを満たしつつ)、許容同時複数のスレッドまたはプロセスがロックを盗むことを試みます

from threading import Thread,Semaphore
import time,random
sm=Semaphore(5)  # 设置5个坑位,同时满足5个线程执行,运行完几个线程,待执行的线程补上对应的位置执行。

def task(name):
    sm.acquire()
    print('%s 正在上厕所' %name)
    time.sleep(random.randint(1,3))
    sm.release()

if __name__ == '__main__':
    for i in range(20):
        t=Thread(target=task,args=('路人%s' %i,))
        t.start()

EVENTイベント

event.isSet():返回event的状态值;

event.wait():如果 event.isSet()==False将阻塞线程;

event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;

event.clear():恢复event的状态值为False。

from threading import Thread,Event
import time

event=Event()

def light():
    print('红灯正亮着')
    time.sleep(3)
    event.set() #绿灯亮

def car(name):
    print('车%s正在等绿灯' %name)
    event.wait() #等灯绿
    print('车%s通行' %name)

if __name__ == '__main__':
    # 红绿灯
    t1=Thread(target=light)
    t1.start()
    # 车
    for i in range(10):
        t=Thread(target=car,args=(i,))
        t.start()
        
  '''
  红灯正亮着
车0正在等绿灯
车1正在等绿灯
车2正在等绿灯
车3正在等绿灯
车4正在等绿灯
车5正在等绿灯
车6正在等绿灯
车7正在等绿灯
车8正在等绿灯
车9正在等绿灯
车1通行
车2通行
车4通行
车5通行
车7通行
车8通行
车0通行
车3通行
车9通行
车6通行
  '''

スレッドキューの3種類

通信プロセスまたはスレッドかどうか、限りの通信ネットワークとして、キューの通信ネットワークに基づいて、情報の相互作用を完了、共有キューの半ばまでに完了し、分散

queue.Queue() # 先进先出
queue.LifoQueue() # 后进先出->堆栈
queue.PriorityQueue() # 优先级
import queue

queue.Queue() #先进先出
q=queue.Queue(3)
q.put(1)
q.put(2)
q.put(3)
print(q.get())
print(q.get())
print(q.get())
'''
1
2
3
'''
import queue

queue.LifoQueue() #后进先出->堆栈
q=queue.LifoQueue(3)
q.put(1)
q.put(2)
q.put(3)
print(q.get())
print(q.get())
print(q.get())
'''
3
2
1
'''
import queue

q=queue.PriorityQueue(3) #优先级,优先级用数字表示,数字越小优先级越高
q.put((10,'a'))
q.put((-1,'b'))
q.put((100,'c'))
print(q.get())
print(q.get())
print(q.get())

'''
(-1, 'b')
(10, 'a')
(100, 'c')
'''

おすすめ

転載: www.cnblogs.com/zhangchaocoming/p/11735912.html