Python基礎(chǔ)教程之多線程編程詳解
進程(process)指的是正在運行的程序的實例,當我們執(zhí)行某個程序時,進程就被操作系統(tǒng)創(chuàng)建了。而線程(thread)則包含于進程之中,是操作系統(tǒng)能夠進行運算調(diào)度的最小單元,多個線程可以同處一個進程中,且同時處理不同的任務(wù)。一條進程中可以并發(fā)多個線程,而同一條線程將共享該進程中的全部系統(tǒng)資源。
每個進程都有自己獨立的地址空間、內(nèi)存和數(shù)據(jù)棧,因此進程之間通訊不方便,所以需使用用進程間通訊(InterProcess Communication, IPC)。而同一個進程中的線程共享資源,因此線程間通訊非常方便,只需注意數(shù)據(jù)同步與互斥的問題。
1. 多線程基礎(chǔ)
線程是進程內(nèi)的執(zhí)行單元,每個進程都至少有一個線程。Python標準庫提供了thread模塊和threading模塊來支持線程編程。在Python3中,thread模塊已經(jīng)改名為_thread
,并在_thread
基礎(chǔ)上開發(fā)了更為強大的threading
模塊,因此我們可以使用threading.Thread
類創(chuàng)建線程對象,并通過start()
方法啟動線程。
import threading def worker(): print("I am a thread.") t = threading.Thread(target=worker) t.start()
在上面的代碼中,我們創(chuàng)建了一個名為worker的函數(shù),它是線程的工作內(nèi)容。我們使用threading.Thread類創(chuàng)建了一個線程對象t,并將worker作為參數(shù)傳遞給了Thread構(gòu)造函數(shù)。然后,我們調(diào)用t.start()方法來啟動線程。當我們運行這段代碼時,會看到以下輸出:
I am a thread.
另外,我們也可以通過繼承Thread類并重寫run
方法來實現(xiàn)線程的創(chuàng)建.
import threading class MyThread(threading.Thread): def __init__(self, n): self.n = n super().__init__() # 調(diào)用父類函數(shù)初始化線程 def run(self): print('線程:', self.n) for i in range(1,3): t = MyThread(i) t.start()
果應(yīng)該輸出打印出:
線程1
線程2
2. 多線程同步
當多個線程同時訪問共享資源時,可能會發(fā)生競態(tài)條件。為避免這種情況,我們需要使用線程同步機制。Python提供了鎖(Lock)、信號量(Semaphore)、事件(Event)等機制來實現(xiàn)線程同步。
下面是一個使用鎖來確保線程同步的示例:
import threading lock = threading.Lock() count = 0 def worker(): global count lock.acquire() try: count += 1 finally: lock.release() threads = [] for i in range(10): t = threading.Thread(target=worker) threads.append(t) for t in threads: t.start() for t in threads: t.join() print(count)
在上面的代碼中,我們定義了一個全局變量count
和一個鎖對象lock
。worker
函數(shù)是每個線程的工作內(nèi)容,它通過lock
來確保count
的修改是原子性的。我們創(chuàng)建了10個線程并啟動它們,等待所有線程執(zhí)行完畢后輸出count
的值。當我們運行這段代碼時,會看到以下輸出:
10
如果把鎖去掉后,count
的值會是幾呢?動手試試吧!會有意向不到的結(jié)果哦~
3. 線程池
線程池可以用來提高代碼的性能,在需要大量線程處理任務(wù)時,可以使用線程池來減少線程的創(chuàng)建和銷毀次數(shù),提高線程的復(fù)用率。
Python
標準庫提供了concurrent.futures
模塊來支持線程池編程。我們可以使用ThreadPoolExecutor
類來創(chuàng)建和管理線程池。
下面是一個使用線程池來處理任務(wù)的示例:
from concurrent.futures import ThreadPoolExecutor def worker(num): return num * num executor = ThreadPoolExecutor(max_workers=4) results = [] for i in range(10): result = executor.submit(worker, i) results.append(result) for result in results: print(result.result())
在上面的代碼中,我們定義了一個worker
函數(shù)用來處理任務(wù)。我們創(chuàng)建了一個ThreadPoolExecutor
對象executor
,并將最大工作線程數(shù)設(shè)置為4
。然后,我們使用submit
方法提交10個任務(wù)給executor
,并將結(jié)果保存在results
列表中。最后,我們遍歷results
列表并輸出每個任務(wù)的結(jié)果。
看到這些個熟悉的單詞...恍惚間有種寫java代碼的感覺。
4. 多進程編程
進程是計算機中程序執(zhí)行的基本單位,而線程則是進程中執(zhí)行代碼的單位。多進程編程就是利用多個獨立運行的進程來完成任務(wù)。相比單進程,多進程有以下優(yōu)點:
- 提高了程序的并發(fā)性能。
- 可以更好地利用多核CPU。
- 能夠處理更大的數(shù)據(jù)集。
在進行多進程編程時,需要注意進程之間的通信和同步問題。
5. Python實現(xiàn)多進程編程
Python標準庫中的multiprocessing
模塊提供了實現(xiàn)多進程編程的工具。其中常用的方法包括:
- Process:創(chuàng)建一個新進程。
- Pool:創(chuàng)建一組進程池。
- Queue:進程之間的消息隊列。
下面是一個簡單的示例,展示如何使用multiprocessing
模塊創(chuàng)建子進程:
import multiprocessing def worker(): """子進程要執(zhí)行的任務(wù)""" print('子進程正在運行') if __name__ == '__main__': p = multiprocessing.Process(target=worker) p.start() print('主進程已經(jīng)結(jié)束')
在這個例子中,我們創(chuàng)建一個新的進程,并將其target
屬性指向worker
函數(shù)。然后使用start
方法啟動該進程。注意到在Windows系統(tǒng)中,需要將啟動進程的代碼放在if __name__ == '__main__':
語句內(nèi),以避免出現(xiàn)一些問題。
5.1 Python進程池
import multiprocessing def worker(num): """子進程要執(zhí)行的任務(wù)""" print(f'子進程{num}正在運行') if __name__ == '__main__': with multiprocessing.Pool(processes=4) as pool: results = pool.map(worker, range(4)) print(results)
在這個例子中,我們使用了進程池來管理多個子進程。首先創(chuàng)建一個Pool對象,其中processes參數(shù)指定了進程池的大小。然后使用map方法提交任務(wù),該方法會自動分配任務(wù)給空閑的子進程并返回結(jié)果。最后打印輸出結(jié)果。
5.2 在進程之間進行通信
import multiprocessing def writer(q): """寫入數(shù)據(jù)""" for i in range(10): q.put(i) def reader(q): """讀取數(shù)據(jù)""" while True: item = q.get() if item is None: break print(item) if __name__ == '__main__': q = multiprocessing.Queue() # 啟動子進程 p1 = multiprocessing.Process(target=writer, args=(q,)) p2 = multiprocessing.Process(target=reader, args=(q,)) p1.start() p2.start() # 等待子進程結(jié)束 p1.join() q.put(None) p2.join()
在這個例子中,我們創(chuàng)建了一個消息隊列,并通過Queue對象將其傳遞給兩個子進程。一個子進程負責(zé)將數(shù)據(jù)寫入隊列中,而另一個子進程則從隊列中讀取數(shù)據(jù)并輸出到屏幕上。注意到在程序末尾我們向隊列中添加了None元素,以便通知讀取數(shù)據(jù)的子進程結(jié)束循環(huán)。
6.多進程計算圓周率
下面是使用Python多進程和蒙特卡羅(Monte Carlo)方法估計圓周率的示例代碼:
import random from multiprocessing import Pool def estimate_pi(n): num_points_inside_circle = 0 for _ in range(n): x = random.uniform(-1, 1) y = random.uniform(-1, 1) if x**2 + y**2 <= 1: num_points_inside_circle += 1 return 4 * num_points_inside_circle / n if __name__ == '__main__': num_processes = 4 num_samples = int(1e8) with Pool(num_processes) as p: results = p.map(estimate_pi, [int(num_samples/num_processes)] * num_processes) print(sum(results)/num_processes)
該代碼使用random
模塊中的uniform
函數(shù)生成在[−1,1][-1, 1][−1,1]范圍內(nèi)均勻分布的隨機數(shù),并統(tǒng)計落入以原點為圓心,半徑為1的圓中的點數(shù)。最后,將所有子進程的結(jié)果相加并除以進程數(shù)以得到圓周率的估計值。我算出的結(jié)果是3.1416664800000005
, 你們呢?
其中,Pool(num_processes)
創(chuàng)建一個具有num_processes
個進程的進程池,p.map(estimate_pi, [int(num_samples/num_processes)] * num_processes)
在每個進程上調(diào)用estimate_pi
函數(shù),并將輸入?yún)?shù)設(shè)為所需的樣本數(shù)量的四分之一,然后返回其結(jié)果。
7.總結(jié)
在Python多線程與進程編程中,我們不僅要理解和掌握的關(guān)于threading
模塊和multiprocessing
模塊如何使用,更要掌握的是線程和進程之間的區(qū)別和聯(lián)系以及線程通信、進程通信的方式。
Python提供了豐富的多線程和多進程編程工具,使我們能夠更有效地利用計算機的多核心處理能力。線程同步和線程池等同步原語可以幫助我們避免競爭條件,并提高程序性能。使用好多線程和多進程,可以使Python編程變得更加高效和靈活。Python爬蟲應(yīng)用中,經(jīng)常會大量使用線程和進程來提升抓取效率哦
以上就是Python基礎(chǔ)教程之多線程編程詳解的詳細內(nèi)容,更多關(guān)于Python多線程編程的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python之Django自動實現(xiàn)html代碼(下拉框,數(shù)據(jù)選擇)
這篇文章主要介紹了Python之Django自動實現(xiàn)html代碼(下拉框,數(shù)據(jù)選擇),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03Windows下實現(xiàn)pytorch環(huán)境搭建
這篇文章主要介紹了Windows下實現(xiàn)pytorch環(huán)境搭建,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04基于Python的socket庫實現(xiàn)通信功能的示例代碼
本文主要給大家介紹了如何使用python的socket庫實現(xiàn)通信功能,這里簡單的給每個客戶端增加一個不重復(fù)的uid,客戶端之間可以根據(jù)這個uid選擇進行廣播通信,感興趣的小伙伴快來看看吧2023-08-08Python K-means實現(xiàn)簡單圖像聚類的示例代碼
本文主要介紹了Python K-means實現(xiàn)簡單圖像聚類的示例代碼,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-10-10