目錄
-
Python并發編程05/ 死鎖/遞歸鎖/信號量/GIL鎖/進程池/線程池
- 1.昨日回顧
-
2.死鎖現象與遞歸鎖
- 2.1死鎖現象
- 2.2遞歸鎖
- 3.信號量
-
4.GIL全局解釋器鎖
- 4.1背景
- 4.2為什么加鎖
- 5.GIL與Lock鎖的區別
-
6.驗證計算密集型IO密集型的效率
- 6.1 IO密集型
- 6.2 計算密集型
-
7.多線程實現socket通信
- 7.1服務端
- 7.2客戶端
- 8.進程池,線程池
Python并發編程05/ 死鎖/遞歸鎖/信號量/GIL鎖/進程池/線程池
1.昨日回顧
#生產者消費者模型.
# 生產者: 產生數據,
# 消費者: 接收數據并做下一步處理
# 容器: 隊列.
#進程, 線程:
# 進程就是資源單位,線程就是執行單位.
#進程與線程的區別:
# 線程: 開銷小,速度快,同一個進程下的線程資源內存級別共享.
# 進程: 開銷巨大,速度慢, 不同進程的數據內存級別不共享.
#join: 阻塞,
# t1.join() 阻塞.
# print('主')
#getname, setname .name
#activeCount() 線程的數量
守護線程: 如果守護線程的生命周期小于其他線程,則他肯定先結束,否則等待其他非守護線程和主線程結束之后結束.
#互斥鎖,鎖
2.死鎖現象與遞歸鎖
#遞歸鎖可以解決死鎖現象,業務需要多個鎖時,先要考慮遞歸鎖
2.1死鎖現象
# from threading import Thread
# from threading import Lock
# import time
#
# lock_A = Lock()
# lock_B = Lock()
#
#
# class MyThread(Thread):
#
# def run(self):
# self.f1()
# self.f2()
#
#
# def f1(self):
#
# lock_A.acquire()
# print(f'{self.name}拿到了A鎖')
#
# lock_B.acquire()
# print(f'{self.name}拿到了B鎖')
#
# lock_B.release()
#
# lock_A.release()
#
# def f2(self):
#
# lock_B.acquire()
# print(f'{self.name}拿到了B鎖')
#
# time.sleep(0.1)
# lock_A.acquire()
# print(f'{self.name}拿到了A鎖')
#
# lock_A.release()
# lock_B.release()
#
# if __name__ == '__main__':
#
# for i in range(3):
# t = MyThread()
# t.start()
2.2遞歸鎖
遞歸鎖有一個計數的功能, 原數字為0,上一次鎖,計數+1,釋放一次鎖,計數-1,
只要遞歸鎖上面的數字不為零,其他線程就不能搶鎖.
# from threading import Thread
# from threading import RLock
# import time
#
# lock_A = lock_B = RLock()
# 遞歸鎖有一個計數的功能, 原數字為0,上一次鎖,計數+1,釋放一次鎖,計數-1,
# 只要遞歸鎖上面的數字不為零,其他線程就不能搶鎖.
# class MyThread(Thread):
#
# def run(self):
# self.f1()
# self.f2()
#
#
# def f1(self):
#
# lock_A.acquire()
# print(f'{self.name}拿到了A鎖')
#
# lock_B.acquire()
# print(f'{self.name}拿到了B鎖')
#
# lock_B.release()
#
# lock_A.release()
#
# def f2(self):
#
# lock_B.acquire()
# print(f'{self.name}拿到了B鎖')
#
# time.sleep(0.1)
# lock_A.acquire()
# print(f'{self.name}拿到了A鎖')
#
# lock_A.release()
#
# lock_B.release()
#
# if __name__ == '__main__':
#
# for i in range(3):
# t = MyThread()
# t.start()
3.信號量
也是一種鎖,控制并發數量
# from threading import Thread, Semaphore, current_thread
# import time
# import random
# sem = Semaphore(5)
#
# def task():
# sem.acquire()
#
# print(f'{current_thread().name} 廁所ing')
# time.sleep(random.randint(1,3))
#
# sem.release()
#
#
# if __name__ == '__main__':
# for i in range(20):
# t = Thread(target=task,)
# t.start()
4.GIL全局解釋器鎖
4.1背景
#理論上來說:單個進程的多線程可以利用多核.
#但是,開發Cpython解釋器的程序員,給進入解釋器的線程加了鎖.
4.2為什么加鎖
1. 當時都是單核時代,而且cpu價格非常貴.
2. 如果不加全局解釋器鎖, 開發Cpython解釋器的程序員就會在源碼內部各種主動加鎖,解鎖,非常麻煩,各種死鎖現象等等.他為了省事兒,直接進入解釋器時給線程加一個鎖.
優點: 保證了Cpython解釋器的數據資源的安全.
缺點: 單個進程的多線程不能利用多核.
#Jpython沒有GIL鎖.
#pypy也沒有GIL鎖.
#現在多核時代, 我將Cpython的GIL鎖去掉行么?
#因為Cpython解釋器所有的業務邏輯都是圍繞著單個線程實現的,去掉這個GIL鎖,幾乎不可能.
單個進程的多線程可以并發,但是不能利用多核,不能并行.
多個進程可以并發,并行.
5.GIL與Lock鎖的區別
相同點: 都是同種鎖,互斥鎖.
不同點:
GIL鎖全局解釋器鎖,保護解釋器內部的資源數據的安全.
GIL鎖 上鎖,釋放無需手動操作.
自己代碼中定義的互斥鎖保護進程中的資源數據的安全.
自己定義的互斥鎖必須自己手動上鎖,釋放鎖.
6.驗證計算密集型IO密集型的效率
6.1 IO密集型
# IO密集型: 單個進程的多線程并發 vs 多個進程的并發并行
# def task():
# count = 0
# time.sleep(random.randint(1,3))
# count += 1
# if __name__ == '__main__':
# 多進程的并發,并行
# start_time = time.time()
# l1 = []
# for i in range(50):
# p = Process(target=task,)
# l1.append(p)
# p.start()
#
# for p in l1:
# p.join()
#
# print(f'執行效率:{time.time()- start_time}') # 8.000000000
# 多線程的并發
# start_time = time.time()
# l1 = []
# for i in range(50):
# p = Thread(target=task,)
# l1.append(p)
# p.start()
#
# for p in l1:
# p.join()
#
# print(f'執行效率:{time.time()- start_time}') # 3.0294392108917236
對于IO密集型: 單個進程的多線程的并發效率高.
6.2 計算密集型
#from threading import Thread
#from multiprocessing import Process
#import time
#import random
# # 計算密集型: 單個進程的多線程并發 vs 多個進程的并發并行
#
# def task():
# count = 0
# for i in range(10000000):
# count += 1
#
#
# if __name__ == '__main__':
#
# # 多進程的并發,并行
# # start_time = time.time()
# # l1 = []
# # for i in range(4):
# # p = Process(target=task,)
# # l1.append(p)
# # p.start()
# #
# # for p in l1:
# # p.join()
# #
# # print(f'執行效率:{time.time()- start_time}') # 3.1402080059051514
#
# # 多線程的并發
# # start_time = time.time()
# # l1 = []
# # for i in range(4):
# # p = Thread(target=task,)
# # l1.append(p)
# # p.start()
# #
# # for p in l1:
# # p.join()
# #
# # print(f'執行效率:{time.time()- start_time}') # 4.5913777351379395
總結: 計算密集型: 多進程的并發并行效率高.
7.多線程實現socket通信
#無論是多線程還是多進程,如果按照上面的寫法,來一個客戶端請求,我就開一個線程,來一個請求開一個線程,
#應該是這樣: 你的計算機允許范圍內,開啟的線程進程數量越多越好.
7.1服務端
# import socket
# from threading import Thread
#
# def communicate(conn,addr):
# while 1:
# try:
# from_client_data = conn.recv(1024)
# print(f'來自客戶端{addr[1]}的消息: {from_client_data.decode("utf-8")}')
# to_client_data = input('>>>').strip()
# conn.send(to_client_data.encode('utf-8'))
# except Exception:
# break
# conn.close()
#
# def _accept():
# server = socket.socket()
# server.bind(('127.0.0.1', 8848))
# server.listen(5)
#
# while 1:
# conn, addr = server.accept()
# t = Thread(target=communicate,args=(conn,addr))
# t.start()
#
# if __name__ == '__main__':
# _accept()
7.2客戶端
# import socket
# client = socket.socket()
# client.connect(('127.0.0.1',8848))
#
# while 1:
# try:
# to_server_data = input('>>>').strip()
# client.send(to_server_data.encode('utf-8'))
#
# from_server_data = client.recv(1024)
# print(f'來自服務端的消息: {from_server_data.decode("utf-8")}')
#
# except Exception:
# break
# client.close()
8.進程池,線程池
#線程池: 一個容器,這個容器限制住你開啟線程的數量,比如4個,第一次肯定只能并發的處理4個任務,只要有任務完成,線程馬上就會接下一個任務.
以時間換空間.
# from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
# import os
# import time
# import random
#
# # print(os.cpu_count())
# def task(n):
# print(f'{os.getpid()} 接客')
# time.sleep(random.randint(1,3))
#
#
# if __name__ == '__main__':
# 開啟進程池 (并行(并行+并發))
# p = ProcessPoolExecutor() # 默認不寫,進程池里面的進程數與cpu個數相等
#
# # p.submit(task,1)
# # p.submit(task,1)
# # p.submit(task,1)
# # p.submit(task,1)
# # p.submit(task,1)
# # p.submit(task,1)
# # p.submit(task,1)
# for i in range(20):
# p.submit(task,i)
#
# 開啟線程池 (并發)
# t = ThreadPoolExecutor() # 默認不寫, cpu個數*5 線程數
# t = ThreadPoolExecutor(100) # 100個線程
# for i in range(20):
# t.submit(task,i)
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
