java多線程并發執行,python多線程并發_Python并發之多線程

 2023-12-06 阅读 34 评论 0

摘要:線程 線程(Thread)也叫輕量級進程,是操作系統能夠進行運算調度的最小單位,它被包含在進程之中,是進程中的實際運作單位。線程自己不擁有系統資源,只擁有一點在運行中必不可少的資源,但它可與同屬的一個進程的其它線程共享進

線程

線程(Thread)也叫輕量級進程,是操作系統能夠進行運算調度的最小單位,它被包含在進程之中,是進程中的實際運作單位。線程自己不擁有系統資源,只擁有一點在運行中必不可少的資源,但它可與同屬的一個進程的其它線程共享進程所擁有的全部資源。一個線程可以創建和撤銷另一個線程,同一進程中的多個線程之間可以并發執行。

java多線程并發執行,多線程

多線程是一個可以提高程序運行效率的方法。一些按順序執行的程序可以使用多線程實現并行執行,從而提高整體效率。然而,多線程也不是可以提升所有程序的執行效率。執行的程序分為CPU密集型和I/O密集型兩種,多線程技術比較適用于后者。

因為在串行結構中讀寫磁盤或者進行網絡通信的時候CPU是閑著的,畢竟網絡比磁盤要慢幾個數量級,磁盤比內存要慢幾個數量級,內存又比CPU慢幾個數量級。

python的多線程。比如你使用單線程對多個網站發送請求,如果這多個網站中的其中2、3個網站的響應速度很慢,而其他網站需要等待這幾個網站加載完成才發送請求,這樣就會影響了整體的效率。而使用多線程并發請求這多個網站,就可以在那些響應較慢的網站加載的同時去請求其他網站,大大提升整個程序的運行效率。

GIL鎖

GIL的全稱是Global Interpreter Lock(全局解釋器鎖),Python最初的設計理念在于,為了解決多線程之間數據完整性和狀態同步的問題,設計為在任意時刻只能由一個線程在解釋器中運行。因此Python中的多線程是表面上的多線程(同一時刻只有一個線程),不是真正的多線程。

python并發、真正意義上的多線程是由CPU來控制的,Python中的多線程是由GIL控制的。如果一個CPU密集型的程序,用C語言寫,運行在一個四核處理器上,采用多線程的話最多可以獲得4倍的效率提升。但是用Python寫的話,效率不會提高,甚至會變慢,因為除了程序本身執行外,還多了線程切換所花的時間。

因此,Python多線程相對更適合寫I/O密集型的程序,真正對效率要求高的CPU密集型程序都用C/C++去寫。

Python多線程

多線程并發。在Python中,我們一般使用threading模塊來實現多進程操作。

threading模塊中包含了關于線程操作的豐富功能,包括:常用的線程函數、線程對象、鎖對象、遞歸鎖對象、事件對象、條件變量對象、信號量對象、定時器對象、柵欄對象。

threading.Thread

我們使用threading模塊中的Thread類來創建線程對象

threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)

下面是Thread的參數說明

group:默認為None(該參數是為了以后實現ThreadGroup類而保留的)

target:在run方法中調用的可調用對象,即需要開啟線程的可調用對象,比如函數或方法。

name:線程名稱,默認為“Thread-N”形式的名稱,N為較小的十進制數。

args:在參數target中傳入的可調用對象的參數元組,默認為空元組()。

kwargs:在參數target中傳入的可調用對象的關鍵字參數字典,默認為空字典{}。

daemon:默認為None,即繼承當前調用者線程(即開啟線程的線程,一般就是主線程)的守護模式屬性,如果不為None,則無論該線程是否為守護模式,都會被設置為“守護模式”。

線程對象的一些重要方法

start():開啟線程活動。它將使得run()方法在一個獨立的控制線程中被調用,需要注意的是同一個線程對象的start()方法只能被調用一次,如果調用多次,則會報RuntimeError錯誤。

run():此方法代表線程活動。

join(timeout=None):讓當前調用者線程(一般為主線程)等待,直到線程結束。

daemon:表示該線程是否為守護線程,True或者False。設置一個線程的daemon必須在線程的start()方法之前,否則會報RuntimeError錯誤。這個值默認繼承自創建它的線程,主線程默認是非守護線程,所以在主線程中創建的線程默認都是非守護線程的,即daemon=False。

直接創建

import time

import random

import threading

def func(name):

s = random.randint(1, 5)

print(f'current thread is {name}, sleeping {s}s.')

time.sleep(s)

print(f'thread {name} is over')

if __name__ == '__main__':

for i in range(1, 5):

t = threading.Thread(target=func, args=(i,))

t.start()

print('Main Thread')

結果如下

current thread is 1, sleeping 5s.

current thread is 2, sleeping 3s.

current thread is 3, sleeping 3s.

current thread is 4, sleeping 2s.

Main Thread

thread 4 is over

thread 2 is over

thread 3 is over

thread 1 is over

上面例子開啟了4個線程,4個線程并發執行任務,先完成任務的線程先輸出結果。

繼承創建

import time

import random

import threading

class Func(threading.Thread):

def __init__(self, name):

super().__init__()

self.name = name

def run(self):

s = random.randint(1, 5)

print(f'current thread is {self.name}, sleeping {s}s.')

time.sleep(s)

print(f'thread {self.name} is over')

if __name__ == '__main__':

for i in range(1, 5):

t = Func(str(i))

t.start()

print('Main Thread')

結果如下

current thread is 1, sleeping 3s.

current thread is 2, sleeping 4s.

current thread is 3, sleeping 3s.

current thread is 4, sleeping 5s.

Main Thread

thread 1 is over

thread 3 is over

thread 2 is over

thread 4 is over

線程對象的start()實際上調用了Func類中的run()方法來開啟一個線程。

join方法的簡單示例

在第一個例子的基礎上加入join方法

import time

import random

import threading

def func(name):

s = random.randint(1, 5)

print(f'current thread is {name}, sleeping {s}s.')

time.sleep(s)

print(f'thread {name} is over')

if __name__ == '__main__':

print('Main Thread start')

tlist = []

for i in range(1, 5):

t = threading.Thread(target=func, args=(i,))

t.start()

tlist.append(t)

for t in tlist:

t.join()

print('do something')

print('Main Thread over')

結果如下

Main Thread start

current thread is 1, sleeping 4s.

current thread is 2, sleeping 1s.

current thread is 3, sleeping 5s.

current thread is 4, sleeping 1s.

thread 2 is over

thread 4 is over

thread 1 is over

thread 3 is over

do something

Main Thread over

先開啟所有子線程,然后放到線程列表tlist中,在對每一個線程調用join()方法來進行阻塞,直到每個子線程執行完畢后,才會繼續執行主線程后面的代碼。

Python線程池

系統啟動一個新線程的成本是很高的,因為它涉及與操作系統的交互。在這種情況下,使用線程池可以很好地提升性能,尤其是當程序中需要創建大量生存期很短暫的線程時,更應該考慮使用線程池。

線程池在系統啟動時即創建大量空閑的線程,程序只要將一個函數提交給線程池,線程池就會啟動一個空閑的線程來執行它。當該函數執行結束后,該線程并不會死亡,而是再次返回到線程池中變成空閑狀態,等待下一個函數。

此外,使用線程池可以有效地控制系統中并發線程的數量。當系統中包含有大量的并發線程時,會導致系統性能急劇下降,甚至導致Python解釋器崩潰,而線程池的最大線程數參數可以控制系統中并發線程的數量不超過此數。

python線程池的使用

在過去,我們可以使用第三方模塊threadpool來創建線程池。但是現在主流的使用線程池的模塊是python3中自帶的模塊concurrent.futures模塊中的ThreadPoolExecutor,如果對threadpool感興趣的小伙伴可以自行搜索相關的信息。

下面主要介紹一下ThreadPoolExecutor的使用方法。

import time

import random

from concurrent.futures import ThreadPoolExecutor

def func(name):

s = random.randint(1, 5)

print(f'current thread is {name}, sleeping {s}s.')

time.sleep(s)

print(f'thread {name} is over')

if __name__ == '__main__':

with ThreadPoolExecutor(max_workers=3) as t:

for i in range(1, 6):

t.submit(func, i)

結果如下

current thread is 1, sleeping 1s.

current thread is 2, sleeping 1s.

current thread is 3, sleeping 2s.

thread 1 is over

current thread is 4, sleeping 2s.

thread 2 is over

current thread is 5, sleeping 4s.

thread 3 is over

thread 4 is over

thread 5 is over

創建一個最大容納數量為3的線程池對象t,通過submit提交執行的函數到線程池中,但線程池中的某個線程(thread 1)執行完成,則把空閑的線程(thread 4)放入到池子中,直到所有線程執行完成則程序結束。

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://808629.com/190394.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 86后生记录生活 Inc. 保留所有权利。

底部版权信息