python常用模塊(Thread,Lock,Queue,ThreadPool)

引入一下線程和進程的理解性概念:

進程: 打開一個程序至少會有一個進程  它是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 - 中止進程池,中止所有任務

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章