目錄
- 一、執行緒
- 1、執行緒理論
- 2、創建執行緒的兩種方式
- 2、1.繼承類創建
- 2、2.使用函式創建
- 3、執行緒的諸多特性
- 二、GIL全域解釋器鎖
- 1、簡介
- 2、驗證GIL的存在
- 3、GIL與普通互斥鎖
- 4、python多執行緒是否有用
- 5、死鎖現象
- 三、信號量
- 1、簡介
- 2、使用方法
- 四、event事件
- 1、簡介
- 2、代碼用法
- 五、行程池與執行緒池
- 1、簡介
- 2、代碼用法
- 六、協程
- 1、簡介
- 2、代碼用法
- 3、協程實作并發
一、執行緒
1、執行緒理論
行程與執行緒的區別:
-
行程:
- 行程是資源單位,表示一塊記憶體空間
-
執行緒:
- 執行緒是執行單位,指在行程內的代碼指令
? 可以將行程比喻成車間,執行緒就是車間里的流水線
? 一個行程內至少含有一個執行緒
執行緒的特點:
? 1、一個行程內可以開設多條執行緒
? 2、同一個行程下的執行緒之間資料是共享的
? 3、創建執行緒的消耗要小于行程(創建時間小于創建行程時間)
2、創建執行緒的兩種方式
2、1.繼承類創建
創建順序:
? 1、匯入 threading import Thread 模塊
? 2、生成一個類
? 3、使用類繼承 Thread
? 4、生成類體代碼
? 5、使用類生成物件,并呼叫
? 6、每次呼叫的物件就是不同的執行緒
代碼用法:
from threading import Thread
class MyThread(Thread):
def run(self):
print('run is running')
time.sleep(1)
print('run is over')
obj = MyThread()
obj.start()
print('主執行緒')
2、2.使用函式創建
創建順序:
? 1、匯入 threading import Thread 模塊
? 2、生成函式(功能代碼)
? 3、定義執行緒物件,并在引數內填入需要加載的子執行緒函式
? 4、使用行程物件加‘點’start()的方式啟動子執行緒
代碼用法:
from threading import Thread
import time
def task(name):
print(f'{name} is running')
time.sleep(0.1)
print(f'{name} is over')
if __name__ == '__main__':
for i in range(100):
t = Thread(target=task, args=('用戶%s'%i,))
t.start()
3、執行緒的諸多特性
1、join()
使異步變為同步,使子執行緒代碼執行結束向下執行主執行緒代碼
2、多個執行緒之間資料共享
3、current_thread()
查看執行緒名字
4、active_count()
查看當前行程下執行緒的數量
二、GIL全域解釋器鎖
1、簡介
官方簡介:
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存在以來,其他特性已經依賴于它強制執行的保證,
'''
簡譯:
1、在cpython解釋器中,存在全域解釋器鎖,簡稱GIL
python解釋器的型別有很多
cpython>>>:基于C開發的python (常用)
jpython>>>:基于Java開發的python
pypython>>>:基于py開發的python
2、GIL本質也是一把互斥鎖,用來阻止同一個行程內多個執行緒的安全
同一個行程下的多個執行緒,如果在沒有互斥鎖的情況下,同樣會產生對資料的處理不準確的情況
3、GIL的存在是因為CPython解釋器中記憶體管理不是執行緒安全的
垃圾回識訓制的本質也是一種執行緒,如果執行緒的本質不是串行的話,那么在我們每將定義一個變數時,就會被垃圾機制立馬檢測,將定義的資料清空
2、驗證GIL的存在
推導流程:
? 1、匯入執行緒模塊
? 2、定義一個全域變數
? 3、定義一個函式體代碼
? 4、生成多個執行緒模塊
? 5、創建的多個執行緒修改全域變數
? 6、得到的結果是多個執行緒依次修改的
結論:
? 執行緒的執行是同步的,如果是異步的話那么修改的全域變數就會產生混亂
代碼表現:
from threading import Thread
num = 100
def task():
global num
num -= 1
t_list = []
for i in range(100):
t = Thread(target=task)
t.start()
t_list.append(t)
for t in t_list:
t.join()
print(num)
3、GIL與普通互斥鎖
? 雖然cpython的執行緒中自帶GIL,但在創建多個執行緒時,任需要考慮到多種情況
- GIL只能確保同行程內的多個執行緒資料不被垃圾回識訓制弄亂
- GIL并不能保證序列里資料的安全
- 在使用執行緒處理資料時,任需要針對各種情況加‘鎖’
代碼表現:
def task(mutex):
global num
mutex.acquire()
count = num
time.sleep(0.1)
num = count - 1
mutex.release()
mutex = Lock()
t_list = []
for i in range(100):
t = Thread(target=task,args=(mutex,))
t.start()
t_list.append(t)
for t in t_list:
t.join()
print(num)
4、python多執行緒是否有用
? 因為cpython的執行緒自帶GIL機制,那么處理資料起來就相對較慢,那么cpython的執行緒是否就很雞肋呢,需要分以下情況
情況一:
單個cpu
多個cpu
情況二:
IO密集型(代碼有IO操作)
計算密集型(代碼沒有IO操作)
1、單個cup + IO密集型
多行程打開記憶體空間較慢,而打開執行緒時間較短,所以這種情況執行緒有較大優勢
# 多執行緒具有優勢
2、單個CPU + 計算密集型
多行程打開記憶體空間較慢,而打開執行緒時間較短,所以這種情況執行緒有較大優勢
# 多執行緒具有優勢
3、多個CPU + IO密集型
多行程打開記憶體空間較慢,而打開執行緒時間較短,所以這種情況執行緒有較大優勢
# 多執行緒具有優勢
4、多個CPU + 計算密集型
多個CPU下行程計算的時間時多個行程的綜合,而執行緒是多個子執行緒的總和
# 多行程具有優勢
'''
總結: IO密集型時多執行緒具有優勢
計算密集型時多行程相對具有優勢
'''
代碼表現:
def work():
# 計算密集型
res = 1
for i in range(1, 100000):
res *= i
if __name__ == '__main__':
# print(os.cpu_count()) # 12 查看當前計算機CPU個數
start_time = time.time()
# p_list = []
# for i in range(12): # 一次性創建12個行程
# p = Process(target=work)
# p.start()
# p_list.append(p)
# for p in p_list: # 確保所有的行程全部運行完畢
# p.join()
t_list = []
for i in range(12):
t = Thread(target=work)
t.start()
t_list.append(t)
for t in t_list:
t.join()
print('總耗時:%s' % (time.time() - start_time)) # 獲取總的耗時
"""
計算密集型
多行程:5.665567398071289
多執行緒:30.233906745910645
"""
def work():
time.sleep(2) # 模擬純IO操作
if __name__ == '__main__':
start_time = time.time()
# t_list = []
# for i in range(100):
# t = Thread(target=work)
# t.start()
# for t in t_list:
# t.join()
p_list = []
for i in range(100):
p = Process(target=work)
p.start()
for p in p_list:
p.join()
print('總耗時:%s' % (time.time() - start_time))
"""
IO密集型
多執行緒:0.0149583816528320
多行程:0.6402878761291504
"""
5、死鎖現象
? 死鎖現象是指,當一個行程下開設了多個‘鎖’時,在多個功能體代碼執行時,經歷搶鎖階段時,如果遇到IO操作,那么就會相互‘尬’住,這種情況就稱之為死鎖現象
代碼表現:
acquire()
release()
from threading import Thread,Lock
import time
mutexA = Lock() # 產生一把鎖
mutexB = Lock() # 產生一把鎖
class MyThread(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
mutexA.acquire()
print(f'{self.name}搶到了A鎖')
mutexB.acquire()
print(f'{self.name}搶到了B鎖')
mutexB.release()
print(f'{self.name}釋放了B鎖')
mutexA.release()
print(f'{self.name}釋放了A鎖')
def func2(self):
mutexB.acquire()
print(f'{self.name}搶到了B鎖')
time.sleep(1)
mutexA.acquire()
print(f'{self.name}搶到了A鎖')
mutexA.release()
print(f'{self.name}釋放了A鎖')
mutexB.release()
print(f'{self.name}釋放了B鎖')
for i in range(10):
obj = MyThread()
obj.start()
三、信號量
1、簡介
? 在python中信號量相當于一次性定義了多把互斥鎖,當我們創建多個執行緒、行程時,就會按照定義好的信號量去執行代碼,當信號量中代碼執行結束后,沒有搶到信號量的行程、代碼就會再次去搶鎖,以此來完成作業
2、使用方法
1、匯入信號量模塊
? 關鍵詞:Semaphore
? from thread import Semaphore
2、產生信號量
3、定義行程或執行緒
4、在功能代碼中加入鎖
代碼用法:
from threading import Thread, Lock, Semaphore
import time
import random
sp = Semaphore(5) # 一次性產生五把鎖
class MyThread(Thread):
def run(self):
sp.acquire()
print(self.name)
time.sleep(random.randint(1, 3))
sp.release()
for i in range(20):
t = MyThread()
t.start()
四、event事件
1、簡介
? event事件是指,可使子行程、執行緒等待主行程、執行緒指令后行程操作
2、代碼用法
1、匯入event模塊
? from thread import evevt
2、定義一個event物件
3、將物件set()方法添加至主執行緒、行程功能代碼后
4、將wait()方法添加至執行緒、行程執行代碼前
代碼用法:
from threading import Thread, Event
import time
event = Event() # 類似于造了一個紅綠燈
def light():
print('紅燈亮著的 所有人都不能動')
time.sleep(3)
print('綠燈亮了 油門踩到底 給我沖!!!')
event.set()
def car(name):
print('%s正在等紅燈' % name)
event.wait()
print('%s加油門 飆車了' % name)
t = Thread(target=light)
t.start()
for i in range(20):
t = Thread(target=car, args=('熊貓PRO%s' % i,))
t.start()
五、行程池與執行緒池
1、簡介
? 行程和執行緒并不是可以無限創建的,因為CPU的公功效時有限的,肆意的創建執行緒、行程會導致計算機死機、崩潰
- 行程池
- 提前創建好固定數量的行程、供后續程式的呼叫,超出則等待
- 執行緒池
- 提前創建號固定數量的執行緒池,供后續程式的呼叫超出則等待
2、代碼用法
1、匯入模塊
? 模塊關鍵詞:concurrent.futures
? 方法關鍵詞:ProcessPoolExecutor (行程
? ThreadPoolExecutor(執行緒)
2、定義行程池或執行緒池最大數量(固定池的數量)
3、直接將任務提交給定義的物件
代碼用法:
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import os
import time
import random
from threading import current_thread
# 1.產生含有固定數量執行緒的執行緒池
# pool = ThreadPoolExecutor(10)
pool = ProcessPoolExecutor(5)
def task(n):
print('task is running')
# time.sleep(random.randint(1, 3))
# print('task is over', n, current_thread().name)
# print('task is over', os.getpid())
return '我是task函式的回傳值'
def func(*args, **kwargs):
print('from func')
if __name__ == '__main__':
# 2.將任務提交給執行緒池即可
for i in range(20):
# res = pool.submit(task, 123) # 朝執行緒池提交任務
# print(res.result()) # 不能直接獲取
# pool.submit(task, 123).add_done_callback(func)
六、協程
1、簡介
- 行程
- 資源單位
- 執行緒
- 執行單位
- 協程
- 可以使單執行緒實作并發且效率比極高
? 協程使程式員自己想出來的辦法,正常情況下代碼遇到IO操作作業系統就會將CPU調走,協程使通過代碼的方法,使程式遇到IO操作時,‘欺騙’CPU,使CPU檢測不到程式的IO操作,繼續向下執行,通過這種方式,實作單執行緒下的高并發
2、代碼用法
1、匯入模塊
? 1、1.模塊關鍵詞:gevent
? 方法關鍵詞:monkey;monkey.patch_all()
? 1、2.模塊關鍵詞:gevent
? 方法關鍵詞:spawn
2、產生模塊物件,后方引數內填入功能名
3、使用物件加‘點’join()的方式呼叫
代碼用法:
import time
from gevent import monkey;
monkey.patch_all() # 固定撰寫 用于檢測所有的IO操作(猴子補丁)
from gevent import spawn
def func1():
print('func1 running')
time.sleep(3)
print('func1 over')
def func2():
print('func2 running')
time.sleep(5)
print('func2 over')
if __name__ == '__main__':
start_time = time.time()
# func1()
# func2()
s1 = spawn(func1) # 檢測代碼 一旦有IO自動切換(執行沒有io的操作 變向的等待io結束)
s2 = spawn(func2)
s1.join()
s2.join()
print(time.time() - start_time) # 8.01237154006958 協程 5.015487432479858
3、協程實作并發
原理是通過‘猴子’補丁的方法監視IO操作的代碼,當代碼遇到IO操作時會自動呼叫下呼叫函式,通過將whil回圈就可以單行程實作并發的效果
代碼用法:
import socket
from gevent import monkey;monkey.patch_all() # 固定撰寫 用于檢測所有的IO操作(猴子補丁)
from gevent import spawn
def communication(sock):
while True:
data = https://www.cnblogs.com/kangssssh/p/sock.recv(1024)
print(data.decode('utf8'))
sock.send(data.upper())
def get_server():
server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)
while True:
sock, addr = server.accept() # IO操作
spawn(communication, sock)
s1 = spawn(get_server)
s1.join()
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/538002.html
標籤:Python
