引入一下線程和進程的理解性概念:
進程: 打開一個程序至少會有一個進程 它是cpu調度的最小的單位。
線程: 程序執行的最小單位,一個進程裏面至少有一個線程,cpu會控制進程裏面的線程。
打個比方:(1)打開一個qq就是一個進程的話,那麼你可以同時和好多人聊天,和一個人聊天這就是一個線程。
(2)再打個比方,一條直行的高速公路,分好幾個車道,這整個告訴公路就相當於一個進程,
那些車道就相當於一個個線程,如果有一條車道上的車拐彎,別的車道的車就要等待,不然就撞車了。
注意:
(1)一個cpu同一時間只能處理一件事,如果同時有多個任務,那麼就輪換着執行,但是每個任務執行的時間非常短暫,無法感受到。
(2)使用線程的時候,不管它的順序,因爲cpu是隨機調度的。
(3)一個程序的執行,就會有一個主線程
階段一:線程的實現
1.線程模塊
Python通過兩個標準庫thread 和threading提供對線程的支持 , threading對thread進行了封裝。threading模塊中提供了Thread , Lock , RLock , Condition等組件。
因此在實際的使用中我們一般都是使用threading
2.Thread類
3.創建線程
在python中創建線程有兩種方式,實例Thread類和繼承重寫Thread類實例Thread類
實例Thread類:
4.創建線程
繼承Thread類
5.Join & setDaemon
(1)
在說這兩個方法之前 , 需要知道主線程與子線程的概念
主線程 : 當一個程序啓動時 , 就有一個線程開始運行 , 該線程通常叫做程序的主線程
子線程 : 因爲程序是開始時就執行的 , 如果你需要再創建線程 , 那麼創建的線程就是這個主線程的子線程
主線程的重要性體現在兩方面 : 1. 是產生其他子線程的線程 2. 通常它必須最後完成執行比如執行各種關閉操作
(2)
join : 阻塞調用程序 , 直到調用join () 方法的線程執行結束, 纔會繼續往下執行
階段二:線程通信
1.互斥鎖
在多線程中 , 所有變量對於所有線程都是共享的 , 因此 , 線程之間共享數據最大的危險在於多個線程同時修改一個變量 , 那就亂套了 , 所以我們需要互斥鎖 , 來鎖住數據。
2.線程間全局變量的共享
提示!因爲線程屬於同一個進程,因此它們之間共享內存區域。因此全局變量是公共的。
3.共享內存間存在競爭問題
from threading import Thread
x = 0
n =1000000
def a(n):
global x
for i in range(n):
x += 1
def b(n):
global x
for i in range(n):
x -= 1
if __name__ == '__main__':
a = Thread(target=a,args = (n,))
b = Thread(target=b,args = (n,))
a.start()
b.start()
a.join()
b.join()
print(x)
提示! 如果1000000不能出現效果可以繼續在後面加0
你會發現這個結果千奇百怪!!!
4.使用鎖來控制共享資源的訪問
下面引入互斥鎖
在多線程中 , 所有變量對於所有線程都是共享的 ,因此 ,線程之間共享數據最大的危險在於多個線程同時修改一個變量 , 那就亂套了 , 所以我們需要互斥鎖 , 來鎖住數據。
只要我們操作全局變量的時候,就在操作之前加鎖,再操作完之後解鎖,就解決了這個資源競爭的問題!!!
5.隊列的基本概念
一個入口,一個出口先入先出(FIFO)
隊列操作一覽:
入隊: put(item)
出隊: get()
測試空: empty()
測試滿: full()
隊列長度: qsize()
任務結束: task_done()
等待完成: join()
注意:
get()等待任務完成,如果不加task_done()則不表示任務完成,只要加這句才表明完成。纔會結束執行。
join就是阻塞,直到這個任務完成(完成的標準就是每次取出都task_done()了)
階段三:線程池
1.池的概念
主線程: 相當於生產者,只管向線程池提交任務。
並不關心線程池是如何執行任務的。
線程池: 相當於消費者,負責接收任務,
並將任務分配到一個空閒的線程中去執行。
因此,並不關心是哪一個線程執行的這個任務。
2.線程池的簡單實現
from threading import Thread
from queue import Queue
import time
class ThreadPool:
def __init__(self,n):
self.queue = Queue()
for i in range(n):
Thread(target = self.worker,daemon = True).start()
def worker(self):
while True:
func,args,kwargs = self.queue.get()
func(*args,*kwargs)
self.queue.task_done()
def apply_async(self,target,args = (),kwargs = {}):
self.queue.put((target,args,kwargs))
def join(self):
self.queue.join()
def fun(x):
print('hello 第%s次'%x)
time.sleep(3)
print('帥哥美女就給點贊啦!')
t = ThreadPool(2)
for i in range(10):
t.apply_async(fun,args = (i,))
t.join()
3.python內置線程池
from multiprocessing.pool import ThreadPool
import time
pool = ThreadPool(2) #創建兩個線程
def funa(x,y):
print('%s好好學習'%x)
time.sleep(3)
print('天天向上')
def funb(x,y):
print('%shello'%x)
time.sleep(3)
print('world')
#我們這就是有一個線程池,裏面有兩個等待處理任務的線程,然後這兩個函數就是兩個任務,
#線程池裏一個線程處理一個,所以會同時輸出!如果多於兩個任務就會執行等待sleep
pool.apply_async(funa,args = ('我們要————',2)) #將任務添加到線程池
pool.apply_async(funb,args = ('大家要————',4))
pool.close() #close之後則無法向線程池提交任務
#內置線程池,自帶守護線程,主線程結束,子線程也跟着結束
#所以需要加阻塞,否則主線程一結束,子線程也跟着結束,無輸出
pool.join() #在join之前可使用終止線程,直接終止線程pool: pool.terminate()
print('這是程序的最後一行,執行到這裏,主線程結束')
4.池的其他操作
操作一: close - 關閉提交通道,不允許再提交任務
操作二: terminate - 中止進程池,中止所有任務