python多任務(wù)-線程

python的thread模塊是比較底層的模塊,python的threading模塊是對(duì)thread做了一些包裝的邮辽,可以更加方便的被使用

查看線程數(shù)量

#coding=utf-8
import threading
from time import sleep,ctime

def sing():
    for i in range(3):
        print("正在唱歌...%d"%i)
        sleep(1)

def dance():
    for i in range(3):
        print("正在跳舞...%d"%i)
        sleep(1)

if __name__ == '__main__':
    print('---開(kāi)始---:%s'%ctime())

    t1 = threading.Thread(target=sing)
    t2 = threading.Thread(target=dance)

    t1.start()
    t2.start()

    while True:
        length = len(threading.enumerate())
        print('當(dāng)前運(yùn)行的線程數(shù)為:%d'%length)
        if length<=1:
            break

        sleep(0.5)

結(jié)果

---開(kāi)始---:Thu Dec 27 11:22:19 2018
正在唱歌...0
正在跳舞...0
當(dāng)前運(yùn)行的線程數(shù)為:3
當(dāng)前運(yùn)行的線程數(shù)為:3
正在唱歌...1
正在跳舞...1
當(dāng)前運(yùn)行的線程數(shù)為:3
當(dāng)前運(yùn)行的線程數(shù)為:3
正在唱歌...2
正在跳舞...2
當(dāng)前運(yùn)行的線程數(shù)為:3
當(dāng)前運(yùn)行的線程數(shù)為:3
當(dāng)前運(yùn)行的線程數(shù)為:1

當(dāng)調(diào)用Thread的時(shí)候不會(huì)創(chuàng)建線程只是創(chuàng)建了一個(gè)對(duì)象,只有在調(diào)用start的時(shí)候才會(huì)創(chuàng)建
通過(guò)使用threading模塊能完成多任務(wù)的程序開(kāi)發(fā)扮宠,為了讓每個(gè)線程的封裝性更完美滥沫,所以使用threading模塊時(shí),往往會(huì)定義一個(gè)新的子類class允跑,只要繼承threading.Thread就可以了王凑,然后重寫run方法,這里調(diào)用start其實(shí)就是調(diào)用了Thread的run方法聋丝。

  • python的threading.Thread類有一個(gè)run方法索烹,用于定義線程的功能函數(shù),可以在自己的線程類中覆蓋該方法弱睦。而創(chuàng)建自己的線程實(shí)例后百姓,通過(guò)Thread類的start方法,可以啟動(dòng)該線程况木,交給python虛擬機(jī)進(jìn)行調(diào)度垒拢,當(dāng)該線程獲得執(zhí)行的機(jī)會(huì)時(shí),就會(huì)調(diào)用run方法執(zhí)行線程火惊。
#coding=utf-8
import threading
import time

class MyThread(threading.Thread):
    def run(self):
        for i in range(3):
            time.sleep(1)
            msg = "I'm "+self.name+' @ '+str(i)
            print(msg)
def test():
    for i in range(5):
        t = MyThread()
        t.start()
if __name__ == '__main__':
    test()

執(zhí)行結(jié)果:

I'm Thread-1 @ 0
I'm Thread-3 @ 0
I'm Thread-2 @ 0
I'm Thread-4 @ 0
I'm Thread-5 @ 0
I'm Thread-1 @ 1
I'm Thread-3 @ 1
I'm Thread-2 @ 1
I'm Thread-4 @ 1
I'm Thread-5 @ 1
I'm Thread-1 @ 2
I'm Thread-2 @ 2
I'm Thread-3 @ 2
I'm Thread-4 @ 2
I'm Thread-5 @ 2

從代碼和執(zhí)行結(jié)果我們可以看出求类,多線程程序的執(zhí)行順序是不確定的。當(dāng)執(zhí)行到sleep語(yǔ)句時(shí)屹耐,線程將被阻塞(Blocked)尸疆,到sleep結(jié)束后,線程進(jìn)入就緒(Runnable)狀態(tài)惶岭,等待調(diào)度寿弱。而線程調(diào)度將自行選擇一個(gè)線程執(zhí)行。上面的代碼中只能保證每個(gè)線程都運(yùn)行完整個(gè)run函數(shù)按灶,但是線程的啟動(dòng)順序症革、run函數(shù)中每次循環(huán)的執(zhí)行順序都不能確定。

總結(jié)
1鸯旁、每個(gè)線程默認(rèn)有一個(gè)名字噪矛,盡管上面的例子中沒(méi)有指定線程對(duì)象的name,但是python會(huì)自動(dòng)為線程指定一個(gè)名字羡亩。
2摩疑、當(dāng)線程的run()方法結(jié)束時(shí)該線程完成。
3畏铆、無(wú)法控制線程調(diào)度程序雷袋,但可以通過(guò)別的方式來(lái)影響線程調(diào)度的方式

互斥鎖

當(dāng)多個(gè)線程幾乎同時(shí)修改某一個(gè)共享數(shù)據(jù)的時(shí)候,需要進(jìn)行同步控制。線程同步能夠保證多個(gè)線程安全訪問(wèn)競(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類老翘,可以方便的處理鎖定:

# 創(chuàng)建鎖
mutex = threading.Lock()

# 鎖定
mutex.acquire()

# 釋放
mutex.release()

注意:
如果這個(gè)鎖之前是沒(méi)有上鎖的,那么acquire不會(huì)堵塞
如果在調(diào)用acquire對(duì)這個(gè)鎖上鎖之前 它已經(jīng)被 其他線程上了鎖锻离,那么此時(shí)acquire會(huì)堵塞铺峭,直到這個(gè)鎖被解鎖為止

import threading
import time

g_num = 0

def test1(num):
    global g_num
    for i in range(num):
        #如果之前沒(méi)被上鎖,那么上鎖成功
        #如果之前已經(jīng)被上鎖了汽纠,那么會(huì)堵塞在這里卫键,直到這個(gè)鎖被解開(kāi)
        mutex.acquire()  # 上鎖
        g_num += 1
        mutex.release()  # 解鎖

    print("---test1---g_num=%d"%g_num)

def test2(num):
    global g_num
    for i in range(num):
        mutex.acquire()  # 上鎖
        g_num += 1
        mutex.release()  # 解鎖

    print("---test2---g_num=%d"%g_num)

# 創(chuàng)建一個(gè)互斥鎖
# 默認(rèn)是未上鎖的狀態(tài)
mutex = threading.Lock()

# 創(chuàng)建2個(gè)線程,讓他們各自對(duì)g_num加1000000次
p1 = threading.Thread(target=test1, args=(1000000,))
p1.start()

p2 = threading.Thread(target=test2, args=(1000000,))
p2.start()

# 等待計(jì)算完成
while len(threading.enumerate()) != 1:
    time.sleep(1)

print("2個(gè)線程對(duì)同一個(gè)全局變量操作之后的最終結(jié)果是:%s" % g_num)

運(yùn)行結(jié)果

---test1---g_num=1000000

---test2---g_num=2000000

2個(gè)線程對(duì)同一個(gè)全局變量操作之后的最終結(jié)果是:2000000

第一個(gè)線程上鎖直到執(zhí)行完之后才到第二個(gè)線程繼續(xù)執(zhí)行疏虫。 上鎖的位置不同永罚,產(chǎn)生的效果也會(huì)不同啤呼,如果將子線程對(duì)數(shù)據(jù)操作的代碼進(jìn)行上鎖卧秘,則兩個(gè)線程將交叉對(duì)數(shù)據(jù)進(jìn)行操作,修改示例如下

  for i in range(num):
        mutex.acquire()  # 上鎖
        g_num += 1
        mutex.release()  # 解鎖

運(yùn)行結(jié)果:

---test2---g_num=1768428
---test1---g_num=2000000
2個(gè)線程對(duì)同一個(gè)全局變量操作之后的最終結(jié)果是:2000000

上鎖解鎖過(guò)程
當(dāng)一個(gè)線程調(diào)用鎖的acquire()方法獲得鎖時(shí)官扣,鎖就進(jìn)入“l(fā)ocked”狀態(tài)翅敌。
每次只有一個(gè)線程可以獲得鎖。如果此時(shí)另一個(gè)線程試圖獲得這個(gè)鎖惕蹄,該線程就會(huì)變?yōu)椤癰locked”狀態(tài)蚯涮,稱為“阻塞”,直到擁有鎖的線程調(diào)用鎖的release()方法釋放鎖之后卖陵,鎖進(jìn)入“unlocked”狀態(tài)遭顶。
線程調(diào)度程序從處于同步阻塞狀態(tài)的線程中選擇一個(gè)來(lái)獲得鎖,并使得該線程進(jìn)入運(yùn)行(running)狀態(tài)泪蔫。

總結(jié)
鎖的好處:

  • 確保了某段關(guān)鍵代碼只能由一個(gè)線程從頭到尾完整地執(zhí)行

鎖的壞處:

  • 阻止了多線程并發(fā)執(zhí)行棒旗,包含鎖的某段代碼實(shí)際上只能以單線程模式執(zhí)行,效率就大大地下降了
  • 由于可以存在多個(gè)鎖撩荣,不同的線程持有不同的鎖铣揉,并試圖獲取對(duì)方持有的鎖時(shí)饶深,可能會(huì)造成死鎖
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市逛拱,隨后出現(xiàn)的幾起案子敌厘,更是在濱河造成了極大的恐慌,老刑警劉巖朽合,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件俱两,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡曹步,警方通過(guò)查閱死者的電腦和手機(jī)锋华,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)箭窜,“玉大人毯焕,你說(shuō)我怎么就攤上這事』怯#” “怎么了纳猫?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)竹捉。 經(jīng)常有香客問(wèn)我芜辕,道長(zhǎng),這世上最難降的妖魔是什么块差? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任侵续,我火速辦了婚禮,結(jié)果婚禮上憨闰,老公的妹妹穿的比我還像新娘状蜗。我一直安慰自己,他們只是感情好鹉动,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布轧坎。 她就那樣靜靜地躺著,像睡著了一般泽示。 火紅的嫁衣襯著肌膚如雪缸血。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,764評(píng)論 1 290
  • 那天械筛,我揣著相機(jī)與錄音捎泻,去河邊找鬼。 笑死埋哟,一個(gè)胖子當(dāng)著我的面吹牛笆豁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼渔呵,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼怒竿!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起扩氢,我...
    開(kāi)封第一講書(shū)人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤耕驰,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后录豺,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體朦肘,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年双饥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了媒抠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡咏花,死狀恐怖趴生,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情昏翰,我是刑警寧澤苍匆,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站棚菊,受9級(jí)特大地震影響浸踩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜统求,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一检碗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧码邻,春花似錦折剃、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)浪谴。三九已至开睡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間苟耻,已是汗流浹背篇恒。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留凶杖,地道東北人胁艰。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親腾么。 傳聞我的和親對(duì)象是個(gè)殘疾皇子奈梳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

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

  • 一、線程介紹 1.1解虱、線程攘须,有時(shí)被稱為輕量進(jìn)程(Lightweight Process,LWP)殴泰,是程序執(zhí)行流的最...
    IIronMan閱讀 1,322評(píng)論 0 2
  • 線程 操作系統(tǒng)線程理論 線程概念的引入背景 進(jìn)程 之前我們已經(jīng)了解了操作系統(tǒng)中進(jìn)程的概念悍汛,程序并不能單獨(dú)運(yùn)行捞魁,只有...
    go以恒閱讀 1,635評(píng)論 0 6
  • 一文讀懂Python多線程 1、線程和進(jìn)程 計(jì)算機(jī)的核心是CPU离咐,它承擔(dān)了所有的計(jì)算任務(wù)谱俭。它就像一座工廠,時(shí)刻在運(yùn)...
    星丶雲(yún)閱讀 1,448評(píng)論 0 4
  • 1.進(jìn)程和線程 隊(duì)列:1宵蛀、進(jìn)程之間的通信: q = multiprocessing.Queue()2旺上、...
    一只寫程序的猿閱讀 1,103評(píng)論 0 17
  • 1.多任務(wù) 在計(jì)算機(jī)中,操作系統(tǒng)可以同時(shí)運(yùn)行多個(gè)任務(wù),這就是多任務(wù)糖埋。那么如何解決多個(gè)任務(wù)同時(shí)運(yùn)行呢宣吱,那就需要用到多...
    瀟瀟雨歇_安然閱讀 371評(píng)論 0 1