標籤:子進程 sleep 空間 import 執行 setname python roc oba
一、並發編程之多線程
1、線程簡單介紹
進程是資源單位,把所有資源集中到一起,而線程是執行單位,真正執行的是線程
每個進程都有一個地址空間,而且預設就有一個控制線程
多線程:在一個進程中存在多個控制線程,多個控制線程共用該進程的地址空間。進程之間是競爭關係,線程之間是協作關係
線程的建立開銷比進程小很多,運行較快
主線程從執行層面上代表了其所在進程的執行過程
2、線程開啟方式
方式一:使用替換threading模組提供的Thread
from threading import Thread
def task():
print(‘is running‘)
if __name__ == ‘__main__‘:
t=Thread(target=task,)
t.start()
print(‘主‘)
方式二:自訂類,繼承Thread
from threading import Thread
class MyThread(Thread):
def __init__(self,name):
super().__init__()
self.name=name
def run(self):
print(‘%s is running‘ %self.name)
if __name__ == ‘__main__‘:
t=MyThread(‘egon‘)
t.start()
print(‘主‘)
3、線程與進程的pid
from threading import Thread
from multiprocessing import Process
import os
def task():
print(‘%s is running‘ %os.getpid())
if __name__ == ‘__main__‘:
# t1=Thread(target=task,) #在主進程下開啟多個線程,每個線程都跟主進程的pid一樣
# t2=Thread(target=task,)
t1=Process(target=task,) #開啟多個進程,每個進程都有不同的pid
t2=Process(target=task,)
t1.start()
t2.start()
print(‘主‘,os.getpid())
4、多線程共用同一個進程內的資源
from threading import Thread
from multiprocessing import Process
n=100
def work():
global n
n=0
if __name__ == ‘__main__‘:
# p=Process(target=work,)
# p.start()
# p.join()
# print(‘主‘,n) #子進程p已經將自己的全域的n改成了0,但改的是它自己的,父進程仍然是100
t=Thread(target=work,)
t.start()
t.join()
print(‘主‘,n) #查看結果為0,同一進程內的線程之間共用進程內的資料
5、Thread對象其他相關的屬性或方法
isAlive():返回線程是否活動
getName():返回線程名
setName():設定線程名
current_thread的用法
from threading import Thread,activeCount,enumerate,current_thread
from multiprocessing import Process
import time
def task():
print(‘%s is running‘ %current_thread().getName())
time.sleep(2)
if __name__ == ‘__main__‘:
p=Process(target=task)
p.start()
print(current_thread())
6、守護線程
進程跟線程都會遵循:守護xxx會等待主xxx運行完畢後被銷毀
主進程:運行完畢指的是主進程代碼運行完畢
主線程:運行完畢指的是主線程所在的進程內所有非守護線程統統運行完畢,主線程才算運行完畢
注意:運行完畢並非終止運行
from threading import Thread
import time
def task1():
print(‘123‘)
time.sleep(10)
print(‘123done‘)
def task2():
print(‘456‘)
time.sleep(1)
print(‘456done‘)
if __name__ == ‘__main__‘:
t1=Thread(target=task1)
t2=Thread(target=task2)
t1.daemon=True
t1.start()
t2.start()
print(‘主‘)
7、GIL全域解譯器鎖
GIL本質是一把互斥鎖,將並發運行變成串列,以此來控制同一時間內共用資料只能被一個任務所修改,進而保證資料安全。
在Cpython解譯器中,同一個進程下開啟的多線程,同一時刻只能有一個線程執行,無法利用多核優勢
from threading import Thread
n=100
def task():
print(‘is running‘)
if __name__ == ‘__main__‘:
t1=Thread(target=task,)
t2=Thread(target=task,)
t3=Thread(target=task,)
# t=Process(target=task,)
t1.start()
t2.start()
t3.start()
print(‘主‘)
多個線程先訪問到解譯器的代碼,即拿到執行許可權,然後將target的代碼交給解譯器的代碼去執行
解譯器的代碼是所有線程共用的,所以記憶體回收線程也可能訪問到解譯器的代碼而去執行,這就導致了一個問題:對於同一個資料100,可能線程1執行n=100的同時,而記憶體回收執行的是回收100的操作,解決這種問題沒有什麼高明的方法,就是加鎖處理,如的GIL,保證python解譯器同一時間只能執行一個任務的代碼
結論:
1、如果是I/O密集型的,可以開啟多線程
2、如果是要求計算效能,可以利用多核,開啟進程
8、同步鎖
1、線程搶的是CIL鎖,GIL鎖相當於執行許可權,拿到執行許可權後才能拿到互斥鎖lock,其他線程也可以搶到GIL,但如果發現lock仍然沒有被釋放則阻塞,即便是拿到執行許可權GIL也要立刻交出來
2、jion是等待所有,即整體串列,而鎖只是鎖住修改共用資料的部分,即部分串列,要想保住資料安全性的根本原來在於讓並發變成串列,jion與互斥鎖都可以實現,互斥鎖的部分串列效率更高
並發編程之多線程