摘要:可以將它們認(rèn)為是在一個(gè)主進(jìn)程或主線程中并行運(yùn)行的一些迷你進(jìn)程。因此與進(jìn)程相比,線程之間的信息共享和通信更加容易。當(dāng)上鎖的線程執(zhí)行完畢進(jìn)行解鎖,堵塞的線程就爭(zhēng)奪到上鎖權(quán)而進(jìn)行代碼塊的運(yùn)行。
threading模塊 線程簡(jiǎn)述
線程(輕量級(jí)進(jìn)程)與進(jìn)程類似,不過它們是在同一個(gè)進(jìn)程下執(zhí)行的,并共享相同的上下文。可以將它們認(rèn)為是在一個(gè)主進(jìn)程或"主線程"中并行運(yùn)行的一些"迷你進(jìn)程"。
線程包括開始、執(zhí)行順序和結(jié)束三部分。它有一個(gè)指令指針,用于記錄運(yùn)行的上下文。它其他線程運(yùn)行時(shí),它可以被搶占(中斷)和臨時(shí)掛起(睡眠/加鎖)---這種做法叫做讓步(yielding)。
多線程的創(chuàng)建使用Thread類,可以有很多方法來創(chuàng)建線程,其中常用的有:
創(chuàng)建Thread的示例,傳給它一個(gè)函數(shù);
派生Thread的子類,重新run方法,并創(chuàng)建子類的實(shí)例。
示例1:創(chuàng)建Thread的實(shí)例,傳給它一個(gè)函數(shù)
from threading import Thread import time def test(): print("---hello-world---") time.sleep(1) for i in range(5): #創(chuàng)建線程,線程執(zhí)行的任務(wù)是target指定的函數(shù),如果函數(shù)需要傳入?yún)?shù),則可以指定args=(),或者kwargs={} t = Thread(target=test) t.start()
運(yùn)行結(jié)果:
---hello-world--- ---hello-world--- ---hello-world--- ---hello-world--- ---hello-world---
示例2:使用Thread子類創(chuàng)建線程
import threading import time # 自定義類繼承threading類 class myThread(threading.Thread): # 重新run方法 def run(self): for i in range(3): time.sleep(1) msg = "I"m " + self.name+" @ "+str(i) print(msg) if __name__ == "__main__": # 創(chuàng)建線程 t = myThread() t.start()
運(yùn)行結(jié)果:
I"m Thread-1 @ 0 I"m Thread-1 @ 1 I"m Thread-1 @ 2
python的threading.Thread類有一個(gè)run方法,用于定義線程的功能函數(shù),可以在自己的線程類中覆蓋該方法。而創(chuàng)建自己的線程實(shí)例后,通過Thread類的start方法,可以啟動(dòng)該線程,交給python虛擬機(jī)進(jìn)行調(diào)度,當(dāng)該線程獲得執(zhí)行的機(jī)會(huì)時(shí),就會(huì)調(diào)用run方法執(zhí)行線程。
多線程共享全局變量在一個(gè)進(jìn)程中,多個(gè)線程之間是共享全局變量的,即一個(gè)線程修改了全局變量,另外一個(gè)線程在此之后獲取的這個(gè)全局變量是被修改后的。比如下面例子:
from threading import Thread import time num = 100 def thread1(): global num for i in range(3): num += 1 print("I"am Thread1 ." + " my num is "+str(num)) def thread2(): print("I"am Thread2. " +" my num is "+ str(num)) t1 = Thread(target=thread1) t1.start() # 讓程序睡眠1秒鐘,確保線程1執(zhí)行完畢。 time.sleep(1) t2 = Thread(target=thread2) t2.start()
運(yùn)行結(jié)果:
I"am Thread1. my num is 103 I"am Thread2. my num is 103
線程關(guān)于全局變量注意點(diǎn)
在一個(gè)進(jìn)程內(nèi)的所有線程共享全局變量,能夠在不適用其他方式的前提下完成多線程之間的數(shù)據(jù)共享(這點(diǎn)要比多進(jìn)程要好)
一個(gè)進(jìn)程中的各個(gè)線程與主線程共享同一片數(shù)據(jù)空間。因此與進(jìn)程相比,線程之間的信息共享和通信更加容易。在一個(gè)程序中,線程的執(zhí)行是:每個(gè)線程運(yùn)行一小會(huì),然后讓步給其他線程(再次排隊(duì)等待更多的CPU時(shí)間)。在整個(gè)進(jìn)程的執(zhí)行過程中,每個(gè)線程執(zhí)行它自己特定的任務(wù),在必要時(shí)和其他線程進(jìn)行通信。
當(dāng)然,這種共享是有風(fēng)險(xiǎn)的。如果兩個(gè)或多個(gè)線程訪問同一片數(shù)據(jù),由于數(shù)據(jù)的訪問順序不同可能導(dǎo)致結(jié)果不一致。這種情況叫競(jìng)態(tài)條件。辛運(yùn)的是,大多數(shù)線程庫都有一些同步源語,以允許線程管理器控制執(zhí)行和訪問。
同步和互斥鎖 同步一般在多線程代碼中,總有一些函數(shù)或者代碼塊不希望被多個(gè)線程同時(shí)執(zhí)行,如果有兩個(gè)線程運(yùn)行的順序發(fā)生變化,就有可能造成代碼的執(zhí)行軌跡或行為不同,產(chǎn)生不一樣的數(shù)據(jù)。這時(shí)候就需要使用同步了。
同步可以理解為協(xié)同步調(diào),按預(yù)定的先后次序進(jìn)行運(yùn)行,比如你先說,我再講。
示例1:多個(gè)線程對(duì)全局變量修改的bug
from threading import Thread import time num = 0 def work1(): global num for i in range(1000000): num += 1 print("-work1-num:%d"%num) def work2(): global num for i in range(1000000): num += 1 print("-work2-num:%d"%num) t1 = Thread(target=work1) t1.start() #time.sleep(3) t2 = Thread(target=work2) t2.start()
運(yùn)行結(jié)果:
-work1-num:1105962 -work2-num:1150358
這個(gè)程序是兩個(gè)線程同時(shí)對(duì)全局變量num進(jìn)行相加操作,但是因?yàn)槎嗑€程中線程的執(zhí)行順序是不同的,因此出現(xiàn)最后相加結(jié)果不是2000000的結(jié)果。
示例2:避免全局變量被修改的方法
避免上面的情況可以有很多種方法,第一種是將上面time.sleep(3)的注釋去掉,就是在3秒內(nèi)讓線程1執(zhí)行,3s內(nèi)執(zhí)行完畢再執(zhí)行線程2對(duì)num變量進(jìn)行自增。(不過這種方法跟單線程沒區(qū)別,也就沒有意義去創(chuàng)建多線程了...)
第二種就是使用輪詢,代碼示例如下:
from threading import Thread import time num = 0 item = 1 def work1(): global num global item if item == 1: for i in range(1000000): num += 1 item = 0 print("-work1-num:%d"%num) def work2(): global num while True:# 輪詢,一直查看條件是否滿足,線程2一直在執(zhí)行... if item != 1: for i in range(1000000): num += 1 break print("-work2-num:%d"%num) t1 = Thread(target=work1) t1.start() #time.sleep(3) t2 = Thread(target=work2) t2.start()
運(yùn)行結(jié)果:
-work1-num:1000000 -work2-num:200000
這次結(jié)果就正確的了,不過這種方法效率也不高。第三種方法就是鎖了。
互斥鎖當(dāng)多個(gè)線程幾乎同時(shí)修改某一個(gè)共享數(shù)據(jù)的時(shí)候,需要進(jìn)行同步控制
線程同步能夠保證多個(gè)線程安全訪問競(jìng)爭(zhēng)資源,最簡(jiǎn)單的同步機(jī)制是引入互斥鎖。
互斥鎖為資源引入一個(gè)狀態(tài):鎖定/非鎖定。
某個(gè)線程要更改共享數(shù)據(jù)時(shí),先將其鎖定,此時(shí)資源的狀態(tài)為“鎖定”,其他線程不能更改;直到該線程釋放資源,將資源的狀態(tài)變成“非鎖定”,其他的線程才能再次鎖定該資源。互斥鎖保證了每次只有一個(gè)線程進(jìn)行寫入操作,從而保證了多線程情況下數(shù)據(jù)的正確性。
threading模塊定義了Lock類,可以很方便地進(jìn)行鎖定。
#創(chuàng)建鎖 mutex = threading.Lock() #鎖定 mutex.acquire([blocking]) #釋放 mutex.release()
其中,鎖定方法acquire可以有一個(gè)blocking參數(shù)。
如果設(shè)定blocking為True,則當(dāng)前線程會(huì)堵塞,直到獲取到這個(gè)鎖為止(如果沒有指定,那么默認(rèn)為True)
如果設(shè)定blocking為False,則當(dāng)前線程不會(huì)堵
示例:
from threading import Thread,Lock import time num = 0 def work1(): global num # 上鎖 mutex.acquire() for i in range(1000000): num += 1 # 解鎖 mutex.release() print("-work1-num:%d"%num) def work2(): global num mutex.acquire() for i in range(1000000): num += 1 mutex.release() print("-work2-num:%d"%num) # 創(chuàng)建一把鎖,這個(gè)鎖默認(rèn)是沒有上鎖的 mutex = Lock() t1 = Thread(target=work1) t1.start() #time.sleep(3) t2 = Thread(target=work2) t2.start()
運(yùn)行結(jié)果:
-work1-num:1000000 -work2-num:2000000
代碼中定義了一把鎖mutex,線程t1和線程t2都互相競(jìng)爭(zhēng)著這把鎖,誰先上鎖,另一方就上不了鎖而堵塞。當(dāng)上鎖的線程執(zhí)行完畢進(jìn)行解鎖,堵塞的線程就爭(zhēng)奪到上鎖權(quán)而進(jìn)行代碼塊的運(yùn)行。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/42956.html
摘要:最近看前端都展開了幾場(chǎng)而我大知乎最熱語言還沒有相關(guān)。有關(guān)書籍的介紹,大部分截取自是官方介紹。但從開始,標(biāo)準(zhǔn)庫為我們提供了模塊,它提供了和兩個(gè)類,實(shí)現(xiàn)了對(duì)和的進(jìn)一步抽象,對(duì)編寫線程池進(jìn)程池提供了直接的支持。 《流暢的python》閱讀筆記 《流暢的python》是一本適合python進(jìn)階的書, 里面介紹的基本都是高級(jí)的python用法. 對(duì)于初學(xué)python的人來說, 基礎(chǔ)大概也就夠用了...
摘要:擴(kuò)展支持多用戶并發(fā)訪問與線程池。項(xiàng)目請(qǐng)見初學(xué)網(wǎng)絡(luò)編程之服務(wù)器。不允許超過磁盤配額。該文件是一個(gè)使用模塊編寫的線程池類。這一步就做到了線程池的作用。 對(duì)MYFTP項(xiàng)目進(jìn)行升級(jí)。擴(kuò)展支持多用戶并發(fā)訪問與線程池。MYFTP項(xiàng)目請(qǐng)見python初學(xué)——網(wǎng)絡(luò)編程之FTP服務(wù)器。 擴(kuò)展需求 1.在之前開發(fā)的FTP基礎(chǔ)上,開發(fā)支持多并發(fā)的功能2.不能使用SocketServer模塊,必須自己實(shí)現(xiàn)多線...
摘要:首發(fā)于我的博客線程池進(jìn)程池網(wǎng)絡(luò)編程之同步異步阻塞非阻塞后端掘金本文為作者原創(chuàng),轉(zhuǎn)載請(qǐng)先與作者聯(lián)系。在了解的數(shù)據(jù)結(jié)構(gòu)時(shí),容器可迭代對(duì)象迭代器使用進(jìn)行并發(fā)編程篇二掘金我們今天繼續(xù)深入學(xué)習(xí)。 Python 算法實(shí)戰(zhàn)系列之棧 - 后端 - 掘金原文出處: 安生??? 棧(stack)又稱之為堆棧是一個(gè)特殊的有序表,其插入和刪除操作都在棧頂進(jìn)行操作,并且按照先進(jìn)后出,后進(jìn)先出的規(guī)則進(jìn)行運(yùn)作。 如...
摘要:本文重點(diǎn)掌握異步編程的相關(guān)概念了解期物的概念意義和使用方法了解中的阻塞型函數(shù)釋放的特點(diǎn)。一異步編程相關(guān)概念阻塞程序未得到所需計(jì)算資源時(shí)被掛起的狀態(tài)。 導(dǎo)語:本文章記錄了本人在學(xué)習(xí)Python基礎(chǔ)之控制流程篇的重點(diǎn)知識(shí)及個(gè)人心得,打算入門Python的朋友們可以來一起學(xué)習(xí)并交流。 本文重點(diǎn): 1、掌握異步編程的相關(guān)概念;2、了解期物future的概念、意義和使用方法;3、了解Python...
閱讀 1018·2021-10-27 14:14
閱讀 1794·2021-10-11 10:59
閱讀 1382·2019-08-30 13:13
閱讀 3209·2019-08-29 15:17
閱讀 2810·2019-08-29 13:48
閱讀 541·2019-08-26 13:36
閱讀 2137·2019-08-26 13:25
閱讀 906·2019-08-26 12:24