python基礎(chǔ)篇:多線程的基本使用

Python多線程是一種并發(fā)編程的方式,可以讓程序同時(shí)執(zhí)行多個(gè)任務(wù)苗膝。在Python中,多線程可以使用標(biāo)準(zhǔn)庫中的threading模塊來實(shí)現(xiàn)。本文將介紹如何使用threading模塊來創(chuàng)建和管理線程伶跷。

創(chuàng)建線程

在Python中,創(chuàng)建線程可以通過創(chuàng)建Thread對(duì)象來實(shí)現(xiàn)秘狞。Thread對(duì)象有一個(gè)target參數(shù)叭莫,指定線程要執(zhí)行的函數(shù)。例如:

import threading

def print_numbers():
    for i in range(10):
        print(i)

thread = threading.Thread(target=print_numbers)
thread.start()

在這個(gè)例子中烁试,我們創(chuàng)建了一個(gè)名為print_numbers的函數(shù)雇初,并將其作為Thread對(duì)象的target參數(shù)傳遞。然后我們調(diào)用start()方法來啟動(dòng)線程减响。當(dāng)線程啟動(dòng)后靖诗,它將調(diào)用print_numbers函數(shù)來執(zhí)行任務(wù)。

傳遞參數(shù)

有時(shí)候我們需要在線程中傳遞參數(shù)支示】伲可以通過在Thread對(duì)象的args參數(shù)中傳遞參數(shù)。例如:

import threading

def print_numbers(start, end):
    for i in range(start, end):
        print(i)

thread = threading.Thread(target=print_numbers, args=(1, 10))
thread.start()

在這個(gè)例子中颂鸿,我們傳遞了start和end兩個(gè)參數(shù)給print_numbers函數(shù)促绵。將這些參數(shù)作為元組傳遞給args參數(shù),即args=(1, 10)。在print_numbers函數(shù)中绞愚,我們使用這些參數(shù)來打印數(shù)字叙甸。

線程同步

在多線程編程中,線程之間可能會(huì)訪問共享資源位衩。如果沒有適當(dāng)?shù)耐綑C(jī)制裆蒸,可能會(huì)導(dǎo)致競(jìng)態(tài)條件和死鎖等問題。Python提供了多種同步機(jī)制糖驴,例如鎖(Lock)僚祷、信號(hào)量(Semaphore)和條件變量(Condition)等。這里以鎖為例贮缕,介紹如何在Python中使用鎖來實(shí)現(xiàn)線程同步辙谜。

import threading

x = 0
lock = threading.Lock()

def increment():
    global x
    for i in range(100000):
        lock.acquire()
        x += 1
        lock.release()

def decrement():
    global x
    for i in range(100000):
        lock.acquire()
        x -= 1
        lock.release()

thread1 = threading.Thread(target=increment)
thread2 = threading.Thread(target=decrement)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(x)

在這個(gè)例子中,我們定義了兩個(gè)函數(shù)increment和decrement感昼,分別對(duì)全局變量x進(jìn)行加一和減一操作装哆。為了避免競(jìng)態(tài)條件,我們使用Lock對(duì)象來控制對(duì)x的訪問定嗓。在increment和decrement函數(shù)中蜕琴,我們調(diào)用acquire()方法來獲取鎖,然后進(jìn)行相應(yīng)的操作宵溅,最后調(diào)用release()方法來釋放鎖凌简。這樣,每個(gè)線程在訪問x時(shí)都會(huì)先獲得鎖恃逻,從而避免了競(jìng)態(tài)條件雏搂。
在這個(gè)例子中,我們創(chuàng)建了兩個(gè)線程thread1和thread2寇损,分別執(zhí)行increment和decrement函數(shù)凸郑。我們通過調(diào)用start()方法來啟動(dòng)線程,然后通過調(diào)用join()方法來等待線程執(zhí)行完畢润绵。最后线椰,我們打印x的值,以檢查線程同步是否正確尘盼。

線程池

在Python中憨愉,可以使用ThreadPoolExecutor類來創(chuàng)建線程池,以便同時(shí)執(zhí)行多個(gè)任務(wù)卿捎。ThreadPoolExecutor可以自動(dòng)管理線程的數(shù)量和生命周期配紫,從而避免創(chuàng)建過多線程導(dǎo)致系統(tǒng)負(fù)載過高的問題。以下是一個(gè)簡(jiǎn)單的示例:

import concurrent.futures

def print_numbers(start, end):
    for i in range(start, end):
        print(i)

with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
    executor.submit(print_numbers, 1, 5)
    executor.submit(print_numbers, 6, 10)

在這個(gè)例子中午阵,我們使用ThreadPoolExecutor創(chuàng)建了一個(gè)最大工作線程數(shù)為2的線程池躺孝。然后我們通過調(diào)用submit()方法來提交兩個(gè)任務(wù)享扔,分別打印1到5和6到10之間的數(shù)字。submit()方法返回一個(gè)Future對(duì)象植袍,用于表示任務(wù)的執(zhí)行狀態(tài)和結(jié)果惧眠。在這個(gè)例子中,我們沒有使用Future對(duì)象來獲取任務(wù)的結(jié)果于个,而是直接打印數(shù)字氛魁。

Future對(duì)象的作用

Future對(duì)象是在Python標(biāo)準(zhǔn)庫中的concurrent.futures模塊中提供的一種異步編程工具,用于表示一個(gè)尚未完成的異步操作的狀態(tài)和結(jié)果厅篓。

當(dāng)一個(gè)任務(wù)被提交到線程池或者進(jìn)程池中時(shí)秀存,會(huì)立即返回一個(gè)Future對(duì)象。這個(gè)對(duì)象可以用于查詢?nèi)蝿?wù)的狀態(tài)和結(jié)果羽氮。Future對(duì)象有以下幾種狀態(tài):

  • PENDING:任務(wù)尚未開始執(zhí)行或链。
  • RUNNING:任務(wù)正在執(zhí)行。
  • CANCELLED:任務(wù)已被取消档押。
  • FINISHED:任務(wù)已完成澳盐,可能成功也可能失敗。

當(dāng)任務(wù)完成后汇荐,F(xiàn)uture對(duì)象會(huì)保存任務(wù)的結(jié)果洞就,可以通過調(diào)用result()方法來獲取。如果任務(wù)尚未完成掀淘,調(diào)用result()方法會(huì)阻塞當(dāng)前線程,直到任務(wù)完成并返回結(jié)果或者拋出異常油昂。

除了result()方法之外革娄,F(xiàn)uture對(duì)象還提供了一些其他的方法,例如cancel()方法可以用于取消任務(wù)冕碟,done()方法用于檢查任務(wù)是否完成拦惋,add_done_callback()方法可以用于注冊(cè)回調(diào)函數(shù),當(dāng)任務(wù)完成時(shí)自動(dòng)調(diào)用安寺。

綜合演示例子

面是一個(gè)綜合的例子厕妖,演示如何使用concurrent.futures模塊中的ThreadPoolExecutor和Future對(duì)象來實(shí)現(xiàn)并發(fā)下載圖片的功能。

import requests
import concurrent.futures

def download_image(url):
    response = requests.get(url)
    if response.status_code == 200:
        filename = url.split("/")[-1]
        with open(filename, "wb") as f:
            f.write(response.content)
        return filename

urls = [
    "https://picsum.photos/200/300",
    "https://picsum.photos/250/350",
    "https://picsum.photos/300/400",
    "https://picsum.photos/350/450",
    "https://picsum.photos/400/500",
]

with concurrent.futures.ThreadPoolExecutor() as executor:
    futures = [executor.submit(download_image, url) for url in urls]
    for future in concurrent.futures.as_completed(futures):
        filename = future.result()
        if filename:
            print(f"{filename} downloaded successfully!")
  • 在這個(gè)例子中挑庶,我們定義了一個(gè)download_image()函數(shù)言秸,用于下載指定URL對(duì)應(yīng)的圖片,并將圖片保存到本地文件系統(tǒng)中迎捺。然后我們定義了一個(gè)URL列表urls举畸,包含了要下載的五張圖片的URL地址。

  • 接下來凳枝,我們使用ThreadPoolExecutor創(chuàng)建一個(gè)線程池抄沮,并使用submit()方法提交五個(gè)下載任務(wù)。submit()方法返回一個(gè)Future對(duì)象,代表了提交的任務(wù)叛买。我們將所有的Future對(duì)象保存在一個(gè)列表中砂代。

  • 然后,我們使用as_completed()函數(shù)遍歷所有的Future對(duì)象率挣,并在每個(gè)Future對(duì)象完成后調(diào)用result()方法獲取結(jié)果刻伊。如果下載成功,就打印出文件名难礼。

通過使用ThreadPoolExecutor和Future對(duì)象娃圆,我們可以同時(shí)下載多個(gè)圖片,從而提高下載速度蛾茉。同時(shí)讼呢,使用Future對(duì)象可以讓我們方便地處理每個(gè)任務(wù)的結(jié)果,從而實(shí)現(xiàn)更加靈活的異步編程谦炬。

總結(jié)

本文介紹了如何在Python中使用threading模塊來創(chuàng)建和管理線程悦屏,包括傳遞參數(shù)、線程同步和線程池等方面键思。同時(shí)础爬,我們也介紹了一些常見的多線程編程問題,例如競(jìng)態(tài)條件和死鎖等吼鳞,以及如何使用鎖來避免這些問題看蚜。在實(shí)際編程中,需要根據(jù)具體情況選擇適當(dāng)?shù)耐綑C(jī)制和線程池配置赔桌,以提高程序的性能和穩(wěn)定性供炎。

本文由mdnice多平臺(tái)發(fā)布

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市疾党,隨后出現(xiàn)的幾起案子音诫,更是在濱河造成了極大的恐慌,老刑警劉巖雪位,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件竭钝,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡雹洗,警方通過查閱死者的電腦和手機(jī)香罐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來队伟,“玉大人穴吹,你說我怎么就攤上這事∈任辏” “怎么了港令?”我有些...
    開封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵啥容,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我顷霹,道長(zhǎng)咪惠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任淋淀,我火速辦了婚禮遥昧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘朵纷。我一直安慰自己炭臭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開白布袍辞。 她就那樣靜靜地躺著鞋仍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪搅吁。 梳的紋絲不亂的頭發(fā)上威创,一...
    開封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天,我揣著相機(jī)與錄音谎懦,去河邊找鬼肚豺。 笑死,一個(gè)胖子當(dāng)著我的面吹牛界拦,可吹牛的內(nèi)容都是我干的吸申。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼享甸,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼呛谜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起枪萄,我...
    開封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎猫妙,沒想到半個(gè)月后瓷翻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡割坠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年齐帚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片彼哼。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡对妄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出敢朱,到底是詐尸還是另有隱情剪菱,我是刑警寧澤摩瞎,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站孝常,受9級(jí)特大地震影響旗们,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜构灸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一上渴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧喜颁,春花似錦稠氮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至稿茉,卻和暖如春锹锰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背漓库。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工恃慧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人渺蒿。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓痢士,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親茂装。 傳聞我的和親對(duì)象是個(gè)殘疾皇子怠蹂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容