標籤:run 多次 活躍 mon UI 修改 add name .text
線程:一套流水線的運行過程,作業系統是個工廠,裡面有有多個車間(進程),線程就是每個車間生產的流水線,每個進程,預設都會有個線程進程是資源單元,線程是cpu上的調度單位線程的建立開銷小,共用空間進程直接是競爭關係,線程之間是協作關係 一、線程的模組threading模組方式一from threading import Threadfrom multiprocessing import Process def task(): print(‘is running‘) if __name__ == ‘__main__‘: t=Thread(target=task,)#線程 # t=Process(target=task,)#進程 t.start() print(‘主’) 線程的結果:is running主 進程:主is running進程開銷大線程開銷小 方式二from threading import Threadfrom multiprocessing import Processclass 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=Process(target=task,) t.start() print(‘主’) 二、pid:多線程的pid是一樣的多進程 pid不一樣 三、多線程共用同一個進程內的資源:from threading import Threadfrom multiprocessing import Processn=100def work(): global n n=0 if __name__ == ‘__main__‘: # p=Process(target=work,) # p.start() # p.join() # print(‘主‘,n) t=Thread(target=work,) t.start() t.join() print(‘主‘,n)線程的n會變,進程不會。因為線程共用資源,進程不是,而且進程pid不一樣。 四、多線程共用同一進程記憶體位址空間:from threading import Threadmsg_l=[]format_l=[]def talk(): while True: msg=input(‘>>: ‘).strip() msg_l.append(msg) def format(): while True: if msg_l: data=msg_l.pop() format_l.append(data.upper()) def save(): while True: if format_l: data=format_l.pop() with open(‘db.txt‘,‘a‘) as f: f.write(‘%s\n‘ %data) if __name__ == ‘__main__‘: t1=Thread(target=talk) t2=Thread(target=format) t3=Thread(target=save) t1.start() t2.start() t3.start() Threading其他方法:#current_thread:當前的線程對象from threading import Thread,activeCount,enumerate,current_threadimport timedef task(): print(‘%s is running‘ %current_thread().getName()) time.sleep(2) if __name__ == ‘__main__‘: t=Thread(target=task,) t.start() t.join()#等待task運行結束(系統回收完) print(t.is_alive())#返回線程是否活動 print(t.getName())#返回線程名 print(enumerate())#當前活躍的進程對象 print(‘主‘) print(activeCount())#活著的線程數 主線程從執行層面上代表了,其所在進程的執行過程。 五、守護線程無論是進程還是線程,都遵循:守護會等待主運行完畢後被銷毀需要強調的是:運行完畢並非終止運行from threading import Threadimport 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(‘主’) 六、GIL
三個需要注意的點:
1.線程搶的是GIL鎖,GIL鎖相當於執行許可權,拿到執行許可權後才能拿到互斥鎖Lock,其他線程也可以搶到GIL,但如果發現Lock仍然沒有被釋放則阻塞,即便是拿到執行許可權GIL也要立刻交出來
2.join是等待所有,即整體串列,而鎖只是鎖住修改共用資料的部分,即部分串列,要想保證資料安全的根本原理在於讓並發變成串列,join與互斥鎖都可以實現,毫無疑問,互斥鎖的部分串列效率要更高
線程1搶到GIL鎖,拿到執行許可權,開始執行,然後加了一把Lock,還沒有執行完畢,即線程1還未釋放Lock,有可能線程2搶到GIL鎖,開始執行,執行過程中發現Lock還沒有被線程1釋放,於是線程2進入阻塞,被奪走執行許可權,有可能線程1拿到GIL,然後正常執行到釋放Lock。。。這就導致了串列啟動並執行效果
七、互斥鎖
from threading import Thread,Lock
import time
n=100
def work():
time.sleep(0.05)
global n
mutex.acquire()
temp=n
time.sleep(0.1)
n=temp-1
mutex.release()
if __name__ == ‘__main__‘:
mutex=Lock()
l=[]
start=time.time()
for i in range(100):
t=Thread(target=work)
l.append(t)
t.start()
for t in l:
t.join()
print(‘run time:%s value:%s‘ %(time.time()-start,n))
#多進程:
#優點:可以利用多核優勢
#缺點:開銷大
#多線程:
#優點:開銷小
#缺點:不能利用多核優勢
# from threading import Thread
# from multiprocessing import Process
# import time
# #計算密集型
# def work():
# res=1
# for i in range(100000000):
# res+=i
#
# if __name__ == ‘__main__‘:
# p_l=[]
# start=time.time()
# for i in range(4):
# # p=Process(target=work) #6.7473859786987305
# p=Thread(target=work) #24.466399431228638
# p_l.append(p)
# p.start()
# for p in p_l:
# p.join()
#
# print(time.time()-start)
from threading import Thread
from multiprocessing import Process
import time
#IO密集型
def work():
time.sleep(2)
if __name__ == ‘__main__‘:
p_l=[]
start=time.time()
for i in range(400):
# p=Process(target=work) #12.104692220687866
p=Thread(target=work) #2.038116455078125
p_l.append(p)
p.start()
for p in p_l:
p.join()
print(time.time()-start)
八、死結與遞迴鎖所謂死結: 是指兩個或兩個以上的進程或線程在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死結狀態或系統產生了死結,這些永遠在互相等待的進程稱為死結進程,如下就是死結
死結現象
from threading import Thread,Lock,RLock
import time
mutexA=Lock()
mutexB=Lock()
class Mythread(Thread):
def run(self):
self.f1()
self.f2()
def f1(self):
mutexA.acquire()
print(‘\033[45m%s 搶到A鎖\033[0m‘ %self.name)
mutexB.acquire()
print(‘\033[44m%s 搶到B鎖\033[0m‘ %self.name)
mutexB.release()
mutexA.release()
def f2(self):
mutexB.acquire()
print(‘\033[44m%s 搶到B鎖\033[0m‘ %self.name)
time.sleep(1)
mutexA.acquire()
print(‘\033[45m%s 搶到A鎖\033[0m‘ %self.name)
mutexA.release()
mutexB.release()
if __name__ == ‘__main__‘:
for i in range(20):
t=Mythread()
t.start()
遞迴鎖,在Python中為了支援在同一線程中多次請求同一資源,python提供了可重新進入鎖RLock。
這個RLock內部維護著一個Lock和一個counter變數,counter記錄了acquire的次數,從而使得資源可以被多次require。直到一個線程所有的acquire都被release,其他的線程才能獲得資源。上面的例子如果使用RLock代替Lock,則不會發生死結
#遞迴鎖
from threading import Thread,Lock,RLock
import time
mutex=RLock()
class Mythread(Thread):
def run(self):
self.f1()
self.f2()
def f1(self):
mutex.acquire()
print(‘\033[45m%s 搶到A鎖\033[0m‘ %self.name)
mutex.acquire()
print(‘\033[44m%s 搶到B鎖\033[0m‘ %self.name)
mutex.release()
mutex.release()
def f2(self):
mutex.acquire()
print(‘\033[44m%s 搶到B鎖\033[0m‘ %self.name)
time.sleep(1)
mutex.acquire()
print(‘\033[45m%s 搶到A鎖\033[0m‘ %self.name)
mutex.release()
mutex.release()
if __name__ == ‘__main__‘:
for i in range(20):
t=Mythread()
t.start()
九、訊號量
from threading import Thread,current_thread,Semaphore
import time,random
sm=Semaphore(5)
def work():
sm.acquire()
print(‘%s 上廁所‘ %current_thread().getName())
time.sleep(random.randint(1,3))
sm.release()
if __name__ == ‘__main__‘:
for i in range(20):
t=Thread(target=work)
t.start()
十、進程池,線程池
進程池
import requests #pip3 install requests
import os,time
from multiprocessing import Pool
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
def get_page(url):
print(‘<%s> get :%s‘ %(os.getpid(),url))
respone = requests.get(url)
if respone.status_code == 200:
return {‘url‘:url,‘text‘:respone.text}
def parse_page(obj):
dic=obj.result()
print(‘<%s> parse :%s‘ %(os.getpid(),dic[‘url‘]))
time.sleep(0.5)
res=‘url:%s size:%s\n‘ %(dic[‘url‘],len(dic[‘text‘])) #類比解析網頁內容
with open(‘db.txt‘,‘a‘) as f:
f.write(res)
if __name__ == ‘__main__‘:
# p=Pool(4)
p=ProcessPoolExecutor()
urls = [
‘http://www.baidu.com‘,
‘http://www.baidu.com‘,
‘http://www.baidu.com‘,
‘http://www.baidu.com‘,
‘http://www.baidu.com‘,
‘http://www.baidu.com‘,
‘http://www.baidu.com‘,
]
for url in urls:
# p.apply_async(get_page,args=(url,),callback=parse_page)
p.submit(get_page,url).add_done_callback(parse_page)
p.shutdown()
print(‘主進程pid:‘,os.getpid())
線程池
import requests #pip3 install requests
import os,time,threading
from multiprocessing import Pool
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
def get_page(url):
print(‘<%s> get :%s‘ %(threading.current_thread().getName(),url))
respone = requests.get(url)
if respone.status_code == 200:
return {‘url‘:url,‘text‘:respone.text}
def parse_page(obj):
dic=obj.result()
print(‘<%s> parse :%s‘ %(threading.current_thread().getName(),dic[‘url‘]))
time.sleep(0.5)
res=‘url:%s size:%s\n‘ %(dic[‘url‘],len(dic[‘text‘])) #類比解析網頁內容
with open(‘db.txt‘,‘a‘) as f:
f.write(res)
if __name__ == ‘__main__‘:
# p=Pool(4)
p=ThreadPoolExecutor(3)
urls = [
‘http://www.baidu.com‘,
‘http://www.baidu.com‘,
‘http://www.baidu.com‘,
‘http://www.baidu.com‘,
‘http://www.baidu.com‘,
‘http://www.baidu.com‘,
‘http://www.baidu.com‘,
]
for url in urls:
# p.apply_async(get_page,args=(url,),callback=parse_page)
p.submit(get_page,url).add_done_callback(parse_page)
p.shutdown()
print(‘主進程pid:‘,os.getpid())
python3第十天