併發編程-進~線程-03死鎖遞歸鎖和信號量

一丶同步和互斥的概念

【同步】:python

  兩個或兩個以上的進程或線程在運行過程當中協同步調,按預約的前後次序運行。好比 A 任務的運行依賴於 B 任務產生的數據。安全

【互斥】:併發

  一個公共資源同一時刻只能被一個進程或線程使用,多個進程或線程不能同時使用公共資源。app

鎖只是鎖住修改共享數據的部分,即部分串行,要想保證數據安全的根本原理在於讓併發變成串行,join與互斥鎖均可以實現,毫無疑問,互斥鎖的部分串行效率要更高

二丶同步鎖(互斥鎖)的引用

把原來的併發執行變成串行,犧牲了執行效率保證了數據安全dom

這邊的同步鎖至關於互斥鎖,爲了安全修改數據,把整個修改數據的過程變成串行ui

from threading import Thread,Lock
import os,time
def work():
    global n
    lock.acquire()
    temp=n
    time.sleep(0.1)
    n=temp-1
    lock.release()
if __name__ == '__main__':
    lock=Lock()
    n=100
    l=[]
    for i in range(100):
        p=Thread(target=work)
        l.append(p)
        p.start()
    for p in l:
        p.join()

    print(n)    #結果確定爲0,

三丶互斥鎖和join的區別

#不加鎖:併發執行,速度快,數據不安全
from threading import current_thread,Thread,Lock
import os,time
def task():
    global n
    print('%s is running' %current_thread().getName())
    temp=n
    time.sleep(0.5)
    n=temp-1


if __name__ == '__main__':
    n=100
    lock=Lock()
    threads=[]
    start_time=time.time()
    for i in range(100):
        t=Thread(target=task)
        threads.append(t)
        t.start()
    for t in threads:
        t.join()

    stop_time=time.time()
    print('主:%s n:%s' %(stop_time-start_time,n))

'''
Thread-1 is running
Thread-2 is running
......
Thread-100 is running
主:0.5216062068939209 n:99
'''


#不加鎖:未加鎖部分併發執行,加鎖部分串行執行,速度慢,數據安全
from threading import current_thread,Thread,Lock
import os,time
def task():
    #未加鎖的代碼併發運行
    time.sleep(3)
    print('%s start to run' %current_thread().getName())
    global n
    #加鎖的代碼串行運行
    lock.acquire()
    temp=n
    time.sleep(0.5)
    n=temp-1
    lock.release()

if __name__ == '__main__':
    n=100
    lock=Lock()
    threads=[]
    start_time=time.time()
    for i in range(100):
        t=Thread(target=task)
        threads.append(t)
        t.start()
    for t in threads:
        t.join()
    stop_time=time.time()
    print('主:%s n:%s' %(stop_time-start_time,n))

'''
Thread-1 is running
Thread-2 is running
......
Thread-100 is running
主:53.294203758239746 n:0
'''

#有的同窗可能有疑問:既然加鎖會讓運行變成串行,那麼我在start以後當即使用join,就不用加鎖了啊,也是串行的效果啊
#沒錯:在start以後馬上使用jion,確定會將100個任務的執行變成串行,毫無疑問,最終n的結果也確定是0,是安全的,但問題是
#start後當即join:任務內的全部代碼都是串行執行的,而加鎖,只是加鎖的部分即修改共享數據的部分是串行的
#單從保證數據安全方面,兩者均可以實現,但很明顯是加鎖的效率更高.
from threading import current_thread,Thread,Lock
import os,time
def task():
    time.sleep(3)
    print('%s start to run' %current_thread().getName())
    global n
    temp=n
    time.sleep(0.5)
    n=temp-1


if __name__ == '__main__':
    n=100
    lock=Lock()
    start_time=time.time()
    for i in range(100):
        t=Thread(target=task)
        t.start()
        t.join()
    stop_time=time.time()
    print('主:%s n:%s' %(stop_time-start_time,n))

'''
Thread-1 start to run
Thread-2 start to run
......
Thread-100 start to run
主:350.6937336921692 n:0 #耗時是多麼的恐怖
'''
)

四丶死鎖現象

所謂死鎖: 是指兩個或兩個以上的進程或線程在執行過程當中,因爭奪資源而形成的一種互相等待的現象線程

from threading import Lock as Lock
import time
mutexA=Lock()
mutexA.acquire()
mutexA.acquire()
print(123)
mutexA.release()
mutexA.release()

解決方法,遞歸鎖,在Python中爲了支持在同一線程中屢次請求同一資源,python提供了可重入鎖RLock。code

from threading import Thread,Lock,current_thread,RLock
import time
"""
Rlock能夠被第一個搶到鎖的人連續的acquire和release
每acquire一次鎖身上的計數加1
每release一次鎖身上的計數減1
只要鎖的計數不爲0 其餘人都不能搶
"""
# mutexA = Lock()
# mutexB = Lock()
mutexA = mutexB = RLock()  # A B如今是同一把鎖

class MyThread(Thread):
    def run(self):  # 建立線程自動觸發run方法 run方法內調用func1 func2至關於也是自動觸發
        self.func1()
        self.func2()

    def func1(self):
        mutexA.acquire()
        print('%s搶到了A鎖'%self.name)  # self.name等價於current_thread().name
        mutexB.acquire()
        print('%s搶到了B鎖'%self.name)
        mutexB.release()
        print('%s釋放了B鎖'%self.name)
        mutexA.release()
        print('%s釋放了A鎖'%self.name)

    def func2(self):
        mutexB.acquire()
        print('%s搶到了B鎖'%self.name)
        time.sleep(1)
        mutexA.acquire()
        print('%s搶到了A鎖' % self.name)
        mutexA.release()
        print('%s釋放了A鎖' % self.name)
        mutexB.release()
        print('%s釋放了B鎖' % self.name)

for i in range(10):
    t = MyThread()
    t.start()

# class Demo(object):
# #     pass
# #
# # obj1 = Demo()
# # obj2 = Demo()
# # print(id(obj1),id(obj2))
"""
只要類加括號實例化對象
不管傳入的參數是否同樣生成的對象確定不同
單例模式除外

本身千萬不要輕易的處理鎖的問題  
"""

五丶信號量

5.1什麼是線程中的信號量

Semaphore管理一個內置的計數器,
每當調用acquire()時內置計數器-1;
調用release() 時內置計數器+1;
計數器不能小於0;當計數器爲0時,acquire()將阻塞線程直到其餘線程調用release()。對象

實例:(同時只有5個線程能夠得到semaphore,便可以限制最大鏈接數爲5):遞歸

與進程池是徹底不一樣的概念,進程池Pool(4),最大隻能產生4個進程,並且從頭至尾都只是這四個進程,不會產生新的,而信號量是產生一堆線程/進程

# 信號量可能在不一樣的領域中 對應不一樣的知識點
"""
互斥鎖:一個廁所(一個坑位)
信號量:公共廁所(多個坑位)
"""

from threading import Semaphore,Thread
import time
import random

n = 100

sm = Semaphore(5)  # 造了一個含有五個的坑位的公共廁所

def task(name):
    global n
    sm.acquire()
    print('%s佔了一個坑位'%name)
    time.sleep(1)
    # time.sleep(random.randint(1,3))
    n = n - 1
    print(n)
    sm.release()

for i in range(40):
    t = Thread(target=task,args=(i,))
    t.start()
相關文章
相關標籤/搜索