一 線程python
# 什麼是進程 :是計算機資源分配的最小單位
# 什麼是線程
# 線程和進程的關係 :
# 每個進程中都至少有一個線程
#開啓100個線程
1 import os 2 import time 3 from threading import Thread 4 n=100 5 def func(i): 6 global n 7 time.sleep(1) 8 n-=1 9 print(os.getpid(),'thread%s'%i) 10 l=[] 11 for i in range(100): 12 t=Thread(target=func,args=(i,)) 13 t.start() 14 l.append(t) 15 for i in l: 16 i.join() 17 print(n)
n的結果是0
總結:git
#每一個進程裏至少有一個主線程負責執行代碼
# 在主線程中能夠再開啓一個新的線程
# 在同一個進程中就有兩個線程同時在工做了
# 線程纔是CPU調度的最小單位
# 多個線程之間的數據時共享的
二 守護線程
# 主線程結束了以後守護線程也同時結束
# 守護線程會等待主線程徹底結束以後才結束
from threading import Thread import time def foo(): while True: print(123) time.sleep(1) def bar(): print(456) time.sleep(3) print('end456') t1=Thread(target=foo) t2=Thread(target=bar) t1.daemon=True #foo是守護線程 t1.start() t2.start() print("main----") #主線程結束的地方 打印結果: 123 456 main---- 123 123 end456
三 鎖(Lock)github
1普通鎖 lock安全
# 當你的程序中出現了取值計算再賦值的操做 數據不安全 —— 加鎖
1 from threading import Thread,Lock 2 import time 3 def work(): 4 global n 5 6 lock.acquire() 7 temp=n 8 time.sleep(0.1) 9 n=temp-1 10 lock.release() 11 if __name__ == '__main__': 12 lock = Lock() 13 n=100 14 l=[] 15 for i in range(10): 16 p=Thread(target=work) 17 l.append(p) 18 p.start() 19 for i in l: 20 i.join() 21 print(n) 22 23 最後n的值爲90
2 遞歸鎖(Rlock)(不多用)app
1 from threading import RLock 2 3 lock = RLock() 4 lock.acquire() 5 lock.acquire() 6 print(123) 7 lock.release() 8 print(456) 9 lock.release()
總結:dom
# 普通的鎖 在同一個線程中 只能acquire一次
# 因此當acquire兩次的時候就容易出現死鎖現象
# 出現了死鎖現象可使用遞歸鎖去解決問題
# 可是本質上死鎖的出現是由於邏輯的錯誤
# 所以咱們更應該把注意力集中在解決邏輯錯誤
# 而不要在出現錯誤的時候直接用遞歸鎖規避異步
遞歸鎖並無本質上解決死鎖的問題函數
四 線程池(ThreadPoolExecutor)ui
1url
1 import time 2 from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor 3 def func(num): 4 print(num) 5 time.sleep(1) 6 print(num) 7 if __name__ == '__main__': 8 t=ThreadPoolExecutor(20) #20個線程,每次處理20個 9 for i in range(50): 10 t.submit(func,i) #異步提交命令 11 t.shutdown()#同join整個線程池 12 print('done')
2 回調函數(callback)
1 # import time 2 # import random 3 # from concurrent.futures import ThreadPoolExecutor 4 # from threading import current_thread 5 # urls=[ 6 # 'https://www.baidu.com', 7 # 'https://www.python.org', 8 # 'https://www.openstack.org', 9 # 'https://help.github.com/', 10 # 'http://www.sina.com.cn/' 11 # 'http://www.cnblogs.com/' 12 # 'http://www.sogou.com/' 13 # 'http://www.sohu.com/' 14 # ] 15 # 16 # def analies(content): 17 # print('分析網頁',current_thread()) 18 # print(content.result()) 19 # 20 # 21 # def get_url(url): 22 # print('爬取網頁',current_thread()) 23 # time.sleep(random.uniform(1,3)) 24 # # analies(url*10) 25 # return url*10 26 # 27 # t = ThreadPoolExecutor(3) 28 # print('主線程',current_thread()) 29 # for url in urls: 30 # t.submit(get_url,url).add_done_callback(analies) #
#回調函數當執行了get_url以後,獲得了一個url*10 ,而後在當即執行analies函數,並傳給content參數
# concurrent.futures裏面的 callback是由子線程作的
五協程(gevent)
須要手動安裝,第三方模塊
安裝 :pip3 install gevent
協程 把一個線程拆分紅幾個
# 協程 是程序級別調度
# 減輕了操做系統的負擔、加強了用戶對程序的可控性
特色是:只要遇到阻塞就會執行別的任務。例如:
from gevent import monkey;monkey.patch_all() import gevent import time def eat(name): print('%s eat 1' %name) time.sleep(2) print('%s eat 2' %name) def play(name): print('%s play 1' %name) time.sleep(1) print('%s play 2' %name) g1=gevent.spawn(eat,'egon') g2=gevent.spawn(play,'alex') g1.join() g2.join() # gevent.joinall([g1,g2])#第二種寫法 print('主')
執行結果是:
egon eat 1
alex play 1
alex play 2
egon eat 2
主
例2:利用協程批量訪問url
1 from gevent import monkey;monkey.patch_all() #這個是必須加的不然gevent不識別time 2 import gevent 3 import requests 4 import time 5 6 def get_page(url): 7 print('GET: %s' %url) 8 response=requests.get(url) 9 if response.status_code == 200: 10 print('%d bytes received from %s' %(len(response.text),url)) 11 12 13 start_time=time.time() 14 gevent.joinall([ 15 gevent.spawn(get_page,'https://www.python.org/'), 16 gevent.spawn(get_page,'https://www.yahoo.com/'), 17 gevent.spawn(get_page,'https://github.com/'), 18 ]) 19 stop_time=time.time() 20 print('run time is %s' %(stop_time-start_time))