Python學(xué)習(xí)

第十七天

Python多任務(wù)

其實(shí)锅纺,多任務(wù)就是操作系統(tǒng)可以同時(shí)運(yùn)行多個(gè)任務(wù)攀涵。比如說我們可以一邊寫代碼铣耘,一邊聽歌洽沟,一邊上網(wǎng)以故,一邊和別人聊天,這其實(shí)就是多任務(wù)的體現(xiàn)裆操。
多任務(wù)的執(zhí)行方式有兩種:
1怒详、并發(fā):交替執(zhí)行炉媒,是假的多任務(wù),指的是任務(wù)數(shù)多于cpu核數(shù)昆烁,通過操作系統(tǒng)的各種任務(wù)調(diào)度算法吊骤,處理完一個(gè)任務(wù)處理下一個(gè)(時(shí)間輪片執(zhí)行哪個(gè)執(zhí)行哪個(gè)),然后實(shí)現(xiàn)用多個(gè)任務(wù)“一起”執(zhí)行静尼。其實(shí)不是一起執(zhí)行的白粉,只是處理的速度快,看似一起執(zhí)行鼠渺。

2鸭巴、并行:指的是任務(wù)數(shù)小于等于cpu核數(shù),即任務(wù)真的是一起執(zhí)行的拦盹,同時(shí)執(zhí)行鹃祖,是真的多任務(wù),cpu一個(gè)一個(gè)單獨(dú)執(zhí)行普舆。

1恬口、線程

線程可以簡(jiǎn)單理解為同一進(jìn)程中有多個(gè)計(jì)數(shù)器,每個(gè)線程的執(zhí)行時(shí)間不確定沼侣,線程是操作系統(tǒng)調(diào)度執(zhí)行的最小單位祖能。
Python中使用線程,需要引入thread模塊蛾洛,但是這個(gè)模塊在Python 2 中屬于正承旧保可用狀態(tài),但在 Python 3 中處于即將廢棄的狀態(tài)雅潭,雖然還可以用揭厚,但包名被改為 _thread,在Python3中一般使用threading模塊扶供。

1.1筛圆、單線程執(zhí)行

import  time
import  threading

def dance():
    print('小張?jiān)谔?)
if __name__ == '__main__':
    for i in range(5):
        t = threading.Thread(target=dance)
        t.start()  #啟動(dòng)線程執(zhí)行任務(wù)調(diào)用start的時(shí)候start自動(dòng)調(diào)用run方法
        time.sleep(1)  # 1s后執(zhí)行下一次循環(huán)

1.2、多線程執(zhí)行

import  time
import  threading
def tiao():   # 子線程
    for i in range(5):
        print('小李在跳舞')
        time.sleep(1)
def sing():  # 子線程
    for i in range(5):
        print('小張?jiān)诔?)
        time.sleep(1)
def main():  # 主線程
    t1 = threading.Thread(target=tiao) #target=函數(shù)--->指向函數(shù)
    t2 = threading.Thread(target=sing) #target=函數(shù)--->指向函數(shù)
    t1.start()  #啟動(dòng)線程執(zhí)行任務(wù)
    t2.start()  #啟動(dòng)線程執(zhí)行任務(wù)
if __name__ == '__main__':
    main()

注意:
1椿浓、調(diào)用thread太援,不會(huì)創(chuàng)建線程,只是創(chuàng)建一個(gè)對(duì)象扳碍;
2提岔、當(dāng)調(diào)用thread創(chuàng)建出來的實(shí)例對(duì)象的start方法的時(shí)候,才會(huì)創(chuàng)建線程以及讓這個(gè)線程開始運(yùn)行笋敞;
3碱蒙、主線程會(huì)等待子線程結(jié)束之后才會(huì)結(jié)束,主線程一死,子線程也會(huì)死赛惩。線程的調(diào)度是隨機(jī)的哀墓,并沒有先后順序。

1.3喷兼、枚舉函數(shù)enumerate的用法

enumerate()是python的內(nèi)置函數(shù)篮绰,在字典上是枚舉、列舉的意思季惯。
用途:用于將一個(gè)可遍歷的數(shù)據(jù)對(duì)象(如列表吠各、元組或字符串)組合為一個(gè)索引序列,同時(shí)列出數(shù)據(jù)和數(shù)據(jù)下標(biāo)勉抓,一般用在 for 循環(huán)當(dāng)中走孽,通過threading.enumerate()就可以獲取線程列表。

import time
import threading
def tiao():
    for i in range(5):
        print('小李在跳舞')
        time.sleep(1)
def sing():
    for i in range(5):
        print('小張?jiān)诔?)
        time.sleep(1)
def main():
    t1 = threading.Thread(target=tiao)
    t2 = threading.Thread(target=sing)
    t1.start()
    t2.start()
    while True:
        print(threading.enumerate())
        if len(threading.enumerate())<=1:
            break
        time.sleep(1)
if __name__ == '__main__':
    main()

2琳状、多線程-共享全局變量

2.1磕瓷、多線程-共享全局變量

假設(shè)兩個(gè)線程t1和t2都要對(duì)全局變量g_num(默認(rèn)是0)進(jìn)行加1運(yùn)算

import  threading
import  time
g_num = 0
def test1():
    global g_num
    for i in range(100):
       g_num += 1
       print('test1方法,g_num的最后結(jié)果是%d'%(g_num))
def test2():
    global  g_num
    for i in range(100):
        g_num += 1
        print('test2方法,g_num的最后結(jié)果是%d'%(g_num))
if __name__ == '__main__':
    print('線程創(chuàng)建之前,g_num的值是%d'%(g_num))
    t1 = threading.Thread(target=test1)
    t1.start()
    t2 = threading.Thread(target=test2)
    t2.start()
    print('兩個(gè)線程創(chuàng)建之后,g_num的值是%d' %(g_num))
   # test1方法,g_num的最后結(jié)果是125
   # test2方法,g_num的最后結(jié)果是126
   # test2方法,g_num的最后結(jié)果是127
   # test2方法,g_num的最后結(jié)果是128
   # test1方法,g_num的最后結(jié)果是129
   # test1方法,g_num的最后結(jié)果是130
   # test1方法,g_num的最后結(jié)果是131

我們觀察到在test1中參雜這test2,也就說明線程是對(duì)全局變量隨意修改可能造成多線程之間對(duì)全局變量的混亂念逞、甚至數(shù)據(jù)的不正確(即線程非安全)困食,也就是說如果多個(gè)線程同時(shí)對(duì)同一個(gè)全局變量操作,會(huì)出現(xiàn)資源競(jìng)爭(zhēng)問題翎承,從而數(shù)據(jù)結(jié)果會(huì)不正確硕盹。我們可以在一個(gè)線程執(zhí)行的時(shí)候time.sleep(1)來保證線程的正常執(zhí)行(不一定),好處就是在一個(gè)進(jìn)程內(nèi)的所有線程共享全局變量叨咖,很方便在多個(gè)線程間共享數(shù)據(jù)瘩例。

2.2、多線程-共享全局變量-args參數(shù)

其實(shí)就是我們?cè)诙x線程target的時(shí)候甸各,可以傳遞參數(shù)去子線程垛贤。

import  threading
import  time
g_nums = [1,2,3,4,5]
def test1(temp):
    temp.append(44)
    print('test1方法,temp的最后結(jié)果是%s'% str(temp))
def test2(temp):
        print('test2方法,temp的最后結(jié)果是%s'% str(temp))

if __name__ == '__main__':
    print('線程創(chuàng)建之前,g_nums的值是%s'% str(g_nums))
    t1 = threading.Thread(target=test1,args=(g_nums,))
    t1.start()
    time.sleep(1)
    t2 = threading.Thread(target=test2,args=(g_nums,))
    t2.start()
    print('兩個(gè)線程創(chuàng)建之后,g_nums的值是%s' % str(g_nums))

3、線程同步

我們前面說了趣倾,多線程開發(fā)聘惦,難免會(huì)遇見線程間的資源爭(zhēng)奪問題,其實(shí)線程間出現(xiàn)問題的原因歸根到底就是線程1對(duì)某個(gè)數(shù)據(jù)操作的時(shí)候還沒有結(jié)束的時(shí)候儒恋,另一個(gè)線程其實(shí)已經(jīng)獲得了時(shí)間輪片善绎,就如同張三轉(zhuǎn)賬給李四,張三的money已經(jīng)扣除诫尽,但是李四沒有收到禀酱,在數(shù)據(jù)庫中我們用事務(wù)來解決,那么在我們的python代碼中牧嫉,如何同步呢剂跟?就是加鎖,加互斥鎖。

3.1浩聋、互斥鎖

多個(gè)線程對(duì)同一數(shù)據(jù)操作的時(shí)候观蜗,需要進(jìn)行同步控制臊恋,保證多個(gè)線程安全訪問競(jìng)爭(zhēng)資源衣洁,這就是互斥鎖同步機(jī)制。
互斥鎖兩個(gè)狀態(tài):鎖定抖仅、非鎖定坊夫。
也就是線程1對(duì)數(shù)據(jù)操作時(shí),然后加鎖撤卢,此時(shí)資源狀態(tài)為鎖定环凿,其它線程無法對(duì)數(shù)據(jù)進(jìn)行操作,除非線程1對(duì)數(shù)據(jù)操作完畢放吩,將資源狀態(tài)改為非鎖定智听,其他線程才可以鎖定該資源,對(duì)該資源操作渡紫〉酵疲互斥鎖保證每次只有一個(gè)線程對(duì)資源操作,從而保證多線程情況下數(shù)據(jù)的正確性惕澎。

import  threading
import  time
g_num = 0
# 創(chuàng)建一個(gè)互斥鎖莉测,默認(rèn)沒有上鎖
mutex = threading.Lock()
def test1():
    global g_num
    mutex.acquire() #上鎖
    for i in range(100):
       g_num += 1
       print('test1方法,g_num的最后結(jié)果是%d'%(g_num))
    mutex.release()  #解鎖
def test2():
    global  g_num
    mutex.acquire()  # 上鎖
    for i in range(100):
        g_num += 1
        print('test2方法,g_num的最后結(jié)果是%d'%(g_num))
    mutex.release()  # 解鎖
if __name__ == '__main__':
    print('線程創(chuàng)建之前,g_num的值是%d'%(g_num))
    t1 = threading.Thread(target=test1)
    t1.start()

    t2 = threading.Thread(target=test2)
    t2.start()
    print('兩個(gè)線程創(chuàng)建之后,g_num的值是%d' %(g_num))

注意:線程通過mutex.acquire()上鎖的時(shí)候,那么之前沒有上鎖唧喉,那么此時(shí)上鎖成功捣卤,上鎖之前如果已經(jīng)上鎖,那么此時(shí)會(huì)堵塞八孝,直到這個(gè)鎖解開董朝。

3.2、死鎖(了解)

線程間共享多個(gè)資源的時(shí)候干跛,如果兩個(gè)線程分別占有一部分資源并且同時(shí)等待對(duì)方的資源益涧,那么就會(huì)形成死鎖。
造成死鎖的原因可以概括成三句話:
1.不同線程同時(shí)占用自己鎖資源
2.這些線程都需要對(duì)方的鎖資源
3.這些線程都不放棄自己擁有的資源

import  threading
import  time
mutexA = threading.Lock()
mutexB = threading.Lock()
class MyThreadA(threading.Thread):
    def run(self):
        mutexA.acquire() #對(duì)mutexA上鎖
        print('------MyThreadA---start------')
        time.sleep(1) #等待一秒驯鳖,此時(shí)mutexB上鎖
        mutexB.acquire() #這個(gè)時(shí)候堵塞 mutexB已經(jīng)上鎖過了
        print('------MyThreadA---end------')
        mutexB.release() #不釋放鎖 死鎖
        mutexA.release()
class MyThreadB(threading.Thread):
    def run(self):
        mutexB.acquire() #對(duì)mutexB上鎖
        print('------MyThreadB---start------')
        time.sleep(1) #等待一秒闲询,此時(shí)mutexA上鎖
        mutexA.acquire() #這個(gè)時(shí)候堵塞 mutexA已經(jīng)上鎖過了
        print('------MyThreadB---end------')
        mutexA.release() #不釋放鎖 死鎖
        mutexB.release()
if __name__ == '__main__':
    t1 = MyThreadA()
    t2 = MyThreadB()
    t1.start()
    t2.start()
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市浅辙,隨后出現(xiàn)的幾起案子扭弧,更是在濱河造成了極大的恐慌,老刑警劉巖记舆,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鸽捻,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)御蒲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門衣赶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人厚满,你說我怎么就攤上這事府瞄。” “怎么了碘箍?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵遵馆,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我丰榴,道長(zhǎng)货邓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任四濒,我火速辦了婚禮换况,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘盗蟆。我一直安慰自己戈二,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布姆涩。 她就那樣靜靜地躺著挽拂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪骨饿。 梳的紋絲不亂的頭發(fā)上亏栈,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音宏赘,去河邊找鬼绒北。 笑死,一個(gè)胖子當(dāng)著我的面吹牛察署,可吹牛的內(nèi)容都是我干的闷游。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼贴汪,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼脐往!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起扳埂,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤业簿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后阳懂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體梅尤,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡柜思,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了巷燥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赡盘。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖缰揪,靈堂內(nèi)的尸體忽然破棺而出陨享,到底是詐尸還是另有隱情,我是刑警寧澤邀跃,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布霉咨,位于F島的核電站蛙紫,受9級(jí)特大地震影響拍屑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜坑傅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一僵驰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧唁毒,春花似錦蒜茴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至近零,卻和暖如春诺核,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背久信。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工窖杀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人裙士。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓入客,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親腿椎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子桌硫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

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