線程隊列裏面有幾種有意思的算法模型,好比隊列,堆棧,以及有優先級的取數據,是比較好玩的幾個東西.實例見下面:html
xxxxxxxxxx
import queue
# 隊列:先進先出
q = queue.Queue()
q.put('123')
q.put('456')
print(q.get())
print(q.get())
'''
123
456
Process finished with exit code 0
能夠看到,程序到此已經結束,其取數據的順序就是先進先出
比較有趣的是,隊列裏面還有task_done和join兩個方法,其用法和以前所瞭解的JoinableQueue裏面的task_done和join是徹底同樣的,即有幾個put就要有幾個task_done,否則join就會卡主,不會釋放程序
'''
q.task_done()
# q.task_done()
q.join()# 這裏就會卡主,程序不會繼續向下執行,也不會自動結束
'''
123
456
'''
xxxxxxxxxx
import queue
# 堆棧,先進後出
q = queue.LifoQueue()
q.put('123')
q.put('456')
q.put('789')
print(q.get())
print(q.get())
print(q.get())
'''
結果:
789
456
123
'''
xxxxxxxxxx
import queue
# 能夠根據優先級取數據
q = queue.PriorityQueue()
# 這裏要注意,put裏面的第一個參數一般爲int類型,並且值越小優先級越高,包括負數,意思就是全部的負數優先級都高於整數,
q.put((-1, 'a'))
q.put((80, 'b'))
q.put((1, 'c'))
print(q.get())
print(q.get())
'''
結果爲:
(-1, '1')
(1, '3')
(80, '2')
Process finished with exit code 0
'''
定時器,顧名思義,就是定時的意思,咱們設定一個延時,就可讓進程延時這些時間而後再開啓.實例以下:node
xxxxxxxxxx
from threading import Thread, current_thread, Timer
import time
def task():
end = time.time()
print(f'線程執行了')
time.sleep(2)
print(f'線程結束了')
print(f'進程開啓延時了{end - start:6.2f}秒')
if __name__ == '__main__':
start = time.time()
t = Timer(4, task) # Timer()括號裏第一個參數爲延時的時間,第二個爲開啓的線程要運行的函數,這個例子是過4秒後開啓一個線程
t.start()
'''
執行結果爲:
線程執行了
線程結束了
進程開啓延時了 4.00秒
Process finished with exit code 0
到這裏咱們可能會有個疑問,那麼這種延時和直接用time.sleep()有什麼區別呢?
區別就在於time.sleep()是整個程序都暫停了,而Timer只是延時開啓進程,對於別的代碼的運行沒有任何影響,咱們能夠在main程序裏面加入兩個print來證實這一點
'''
from threading import Thread, current_thread, Timer
import time
def task():
end = time.time()
print(f'線程執行了')
time.sleep(2)
print(f'線程結束了')
print(f'進程開啓延時了{end - start:6.2f}秒')
if __name__ == '__main__':
print('進入main')
start = time.time()
print('準備進入延時')
t = Timer(4, task)
t.start()
print('main結束')
'''
執行結果爲:
進入main
準備進入延時
main結束
線程執行了
線程結束了
進程開啓延時了 4.00秒
Process finished with exit code 0
上面的執行結果咱們就能夠看出,延時並無影響其他代碼的運行,這點很是難得
'''
首先咱們要明白進程池和線程池的概念是什麼,所謂池,池子,現實生活中是盛水的,能夠說是一個容器,在咱們的Python裏面也是一個容器,用來容納進程或者線程.python
那麼進程池/線程池的做用就是,當計算機須要併發的任務量遠遠大於計算機所能承受的範圍的時候,或者說計算機沒法一次性開啓過多的任務數的時候,咱們就須要進程池/線程池來限制當前計算機的進程數/線程數,從而保護計算機,或者是服務器,使其不至於宕機,影響業務.web
池所用的模塊是ProcessPoolExecutor和 ThreadPoolExecutor,經常使用的一些方法包括:算法
result()
,等待該進程/線程的返回結果,當結果都返回以後,result()纔會打印出來而後執行後面的代碼,這樣會致使全部的任務變成串行,一個線程執行完纔會去執行下一個線程,雖然下降了效率,可是提升了程序的安全性,條理性也會更清楚編程
submit(obj,*args,**kwargs)
第一個參數就是所調用的函數名,第二個和第三個都是調用的函數所須要傳入的參數,submit會有一個返回值,因此一般所調用的函數會有返回值,來做爲submit的返回值.windows
shutdown()
關閉當前進程池/線程池安全
例程以下:服務器
xxxxxxxxxx
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
from threading import currentThread
from multiprocessing import current_process
import time
def task(i):
print(f'開啓{i}')
print(f'線程{currentThread().name}執行任務{i}')
# print(f'進程{current_process().name}執行任務{i}')
time.sleep(2)
print(f'線程{currentThread().name}結束任務{i}')
print(f"\033[5;31;40m結束{i}\033[0m")
return i ** 2
# print(f'進程{current_process().name}結束任務{i}')
if __name__ == '__main__':
# pool_p = ProcessPoolExecutor(4)
pool_t = ThreadPoolExecutor(4)
fu_list = []
for i in range(5):
# submit括號裏第一個參數是所調用的函數名task,第二個和第三個是須要傳入的調用函數task的形參,
# submit能夠返回一個對象,因此能夠在task函數裏面寫返回值
future = pool_t.submit(task, i) # 5個線程,解決20個任務
print(f'執行結果爲{future.result()}')
# 若是沒有結果,會一直等待拿到結果,就會致使全部的任務變成串行,一個線程運行完纔會運行下一個任務
# pool_p.submit(task, i)
pool_t.shutdown() # 會關閉池的入口,會等到全部的任務執行完,結束阻塞
for fu in fu_list:
print(fu.result())
'''
開啓0
線程ThreadPoolExecutor-0_0執行任務0
線程ThreadPoolExecutor-0_0結束任務0
結束0
執行結果爲0
開啓1
線程ThreadPoolExecutor-0_0執行任務1
線程ThreadPoolExecutor-0_0結束任務1
結束1
執行結果爲1
開啓2
線程ThreadPoolExecutor-0_1執行任務2
線程ThreadPoolExecutor-0_1結束任務2
結束2
執行結果爲4
開啓3
線程ThreadPoolExecutor-0_0執行任務3
線程ThreadPoolExecutor-0_0結束任務3
結束3
執行結果爲9
開啓4
線程ThreadPoolExecutor-0_2執行任務4
線程ThreadPoolExecutor-0_2結束任務4
結束4
執行結果爲16
Process finished with exit code 0
'''
同步和異步實際上是提交任務的兩種方式,簡單來講,併發
簡單來講:協程是一種用戶態的輕量級的線程,是由用戶程序本身控制調度的
協程須要注意的兩點是:
爲了正確的使用協程,咱們須要引入gevent模塊(這個不是Python自帶的模塊,須要讀者本身下載,指令pip3 install gevent),並且須要使用到gevent的一個很是有趣的魔法方法,叫作moncky,具體見下例
xxxxxxxxxx
from gevent import monkey;monkey.patch_all() # 咱們須要在開頭加上這一句,由於gevent自己並不能捕獲全部的IO,只能識別自身的延時,因此這個語句至關於一個補丁,能夠識別程序裏面全部的IO,而後遇到這些IO的時候切換至其餘的協程
import gevent# 導入gevent模塊,這個導入必定要在上面的語句下面,切記!
import time
def eat():
print('eat 1')
time.sleep(2)
print('eat 2')
def play():
print('play 1')
time.sleep(3)
print('play 2')
start = time.time()
g1 = gevent.spawn(eat)# gevent的用法不一樣於以前學習的進程和線程,是另外一種調用方法,並且下面必定須要用join來阻塞才能得到每一個協程的執行結果,不然程序不會返回任何值,也不會執行開啓協程所寫入的那個任務的內容
g2 = gevent.spawn(play)
g1.join()
g2.join()
end = time.time()
print(f'{end - start:6.6f}')