多任務-線程

1.多任務

在計算機中,操作系統(tǒng)可以同時運行多個任務,這就是多任務熊镣。那么如何解決多個任務同時運行呢,那就需要用到多線程募书。多任務可以通過并發(fā)和并行來完成绪囱,那么什么是并發(fā)和并行呢?

? 1.并發(fā):指的是任務數(shù)多于cpu核數(shù)莹捡,通過操作系統(tǒng)的各種任務調(diào)度算法鬼吵,實現(xiàn)用多個任務“一起”執(zhí)行(實際上總有一些任務不在執(zhí)行,因為切換任務的速度相當快篮赢,看上去一起執(zhí)行而已)齿椅,在實際開發(fā)中,并發(fā)是最常用的启泣。

? 2.并行:當任務數(shù)小于或者等于cpu核數(shù)時涣脚,每一個任務都有對應的cpu來處理執(zhí)行,即任務真的是一起執(zhí)行的寥茫。

2.多任務的實現(xiàn)-多線程

實現(xiàn)多任務需要用到多線程遣蚀,那么什么是線程呢?下面是對它的理解:

????1.一個程序運行起來至少有一個進程纱耻,一個進程至少有一個線程

????2.處理器cpu分配給線程芭梯,即cpu真正運行的是線程中的代碼

????3.分配cpu給線程時,是通過時間片輪訓方式進行的

????4.進程是操作系統(tǒng)分配程序執(zhí)行資源的單位弄喘,而線程是進程的一個實體粥帚,

是CPU調(diào)度和分配的單位。

實現(xiàn)多線程的方式有2種方式:

1.python的thread模塊是比較底層的模塊限次,python的threading模塊是對thread做了一些包裝的,可以更加方便的被使用,通過threading模塊可以創(chuàng)建線程

# 引入threading線程模塊

import????threading卖漫,time?

def????download_music():

'''模擬下載歌曲费尽,需要5秒鐘下載完成'''

????for? ? i? ? in? ? range(5):

?????time.sleep(1)# 休眠1秒

????print("---正在下載歌曲%d---"% i)

def????main():

????# 創(chuàng)建線程對象t1 ,target: 指向新開啟的線程要執(zhí)行的代碼

????t1 = threading.Thread(target=download_music)?

? ? t1.start()# 啟動線程,即線程開始執(zhí)行

if__name__ =='__main__':?

?????main()

2.繼承Thread類羊始,創(chuàng)建一個新的class旱幼,將要執(zhí)行的代碼 寫到run函數(shù)里面

import????threading,time

# 自定義類突委,繼承threading.Thread

class????MyThread(threading.Thread):

????def????run(self):

????????for????i????in????range(5):

?????????????time.sleep(1)

????????????# name屬性中保存的是當前線程的名字

? ? ? ? ? ? msg ="I'm "+ self.name +' @ '+ str(i)?

?????????????print(msg)

if__name__ =='__main__':

# 通過MyThread創(chuàng)建線程對象

????t1 = MyThread()

????# 開始執(zhí)行線程

????t1.start()

3.多線程的注意點

1. 線程何時開啟柏卤,何時結(jié)束

1.子線程何時開啟,何時運行

? 當調(diào)用thread.start()時 開啟線程匀油,再運行線程的代碼

2.子線程何時結(jié)束

? 子線程把target指向的函數(shù)中的語句執(zhí)行完畢后缘缚,或者線程中的run函數(shù)代碼執(zhí)行完畢后,立即結(jié)束當前子線程

3.查看當前線程數(shù)量

? 通過threading.enumerate()可枚舉當前運行的所有線程

4.主線程何時結(jié)束

? 所有子線程執(zhí)行完畢后敌蚜,主線程才結(jié)束

2. 線程的執(zhí)行順序

多線程的創(chuàng)建與執(zhí)行都是無序的桥滨。

3.總結(jié)

1.每個線程默認有一個名字,盡管上面的例子中沒有指定線程對象的name弛车,但是python會自動為線程指定一個名字齐媒。

2.當線程的run()方法結(jié)束時該線程完成。

3.無法控制線程調(diào)度程序纷跛,但可以通過別的方式來影響線程調(diào)度的方式喻括。

4.共享全局變量

先來看一段代碼:

from????threading????import????Thread

import????time

g_nums = [11,22,33]

def? ? main():

????def????work1(nums):

????????nums.append(44)?

?????????print("----in work1---",nums)

????def????work2(nums):

????????#延時一會,保證t1線程中的事情做完

????????time.sleep(1)?

?????????print("----in work2---",nums)

if? ? __name__ == '__main__':

????t1 = Thread(target=work1, args=(g_nums,))

????t1.start()

????t2 = Thread(target=work2, args=(g_nums,))

????t2.start()

運行結(jié)果:

----inwork1--- [11,22,33,44]

----inwork2--- [11,22,33,44]

總結(jié):

在一個進程內(nèi)的所有線程共享全局變量贫奠,很方便在多個線程間共享數(shù)據(jù)

缺點就是唬血,多線程對全局變量隨意遂改可能造成全局變量的混亂(即線程非安全)

5.多線程開發(fā)可能遇到的問題

????假設兩個線程t1和t2都要對全局變量g_num(默認是0)進行加1運算,t1和t2都各對g_num加10次叮阅,g_num的最終的結(jié)果應該為20刁品。

????但是由于是多線程同時操作,有可能出現(xiàn)下面情況:

????????在g_num=0時浩姥,t1取得g_num=0挑随。此時系統(tǒng)把t1調(diào)度為”sleeping”狀態(tài),把t2轉(zhuǎn)換為”running”狀態(tài)勒叠,t2也獲得g_num=0

????????然后t2對得到的值進行加1并賦給g_num兜挨,使得g_num=1

????????然后系統(tǒng)又把t2調(diào)度為”sleeping”,把t1轉(zhuǎn)為”running”眯分。線程t1又把它之前得到的0加1后賦值給g_num拌汇。

????????這樣導致雖然t1和t2都對g_num加1,但結(jié)果仍然是g_num=1弊决。

????所以噪舀,如果多個線程同時對同一個全局變量操作魁淳,會出現(xiàn)資源競爭問題,從而數(shù)據(jù)結(jié)果會不正確与倡,即會遇到線程安全問題

6.使用同步機制解決線程安全問題-互斥鎖

同步就是協(xié)同步調(diào)界逛,按預定的先后次序進行運行。如:你說完纺座,我再說息拜。

"同"字從字面上容易理解為一起動作,其實不是净响,"同"字應是指協(xié)同少欺、協(xié)助、互相配合馋贤。

如進程赞别、線程同步,可理解為進程或線程A和B一塊配合掸掸,A執(zhí)行到一定程度時要依靠B的某個結(jié)果氯庆,于是停下來,示意B運行;B執(zhí)行扰付,再將結(jié)果給A;A再繼續(xù)操作堤撵。

在多線程編程里面,一些敏感數(shù)據(jù)不允許被多個線程同時訪問羽莺,此時就使用同步訪問技術(shù)实昨,保證數(shù)據(jù)在任何時刻,最多有一個線程訪問盐固,以保證數(shù)據(jù)的正確性荒给。

互斥鎖:

????當多個線程幾乎同時修改某一個共享數(shù)據(jù)的時候,需要進行同步控制

????線程同步能夠保證多個線程安全訪問競爭資源刁卜,最簡單的同步機制是引入互斥鎖志电。

????互斥鎖為資源引入一個狀態(tài):鎖定/非鎖定

????某個線程要更改共享數(shù)據(jù)時,先將其鎖定蛔趴,此時資源的狀態(tài)為“鎖定”挑辆,其他線程不能更改;直到該線程釋放資源孝情,將資源的狀態(tài)變成“非鎖定”鱼蝉,其他的線程才能再次鎖定該資源◇锏矗互斥鎖保證了每次只有一個線程進行寫入操作魁亦,從而保證了多線程情況下數(shù)據(jù)的正確性。

threading模塊中定義了Lock類羔挡,可以方便的處理鎖定:

# 1.創(chuàng)建鎖

mutex = threading.Lock()

#2. 鎖定

mutex.acquire()

#3. 釋放

mutex.release()

注意:

如果這個鎖之前是沒有上鎖的洁奈,那么acquire不會堵塞

如果在調(diào)用acquire對這個鎖上鎖之前 它已經(jīng)被 其他線程上了鎖间唉,那么此時acquire會堵塞,直到這個鎖被解鎖為止

示例:使用互斥鎖完成2個線程對同一個全局變量各加100萬次的操作

import????threading睬魂,time

g_num =0

def????test1(num):

????global????g_num

????for????i????in????range(num):

? ? ? ? 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)建一個互斥鎖终吼,默認是未上鎖的狀態(tài)

mutex = threading.Lock()

# 創(chuàng)建2個線程,讓他們各自對g_num加1000000次

p1 = threading.Thread(target=test1, args=(1000000,))

p1.start()

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

p2.start()

# 等待計算完成

while????len(threading.enumerate()) !=1:?

?????time.sleep(1)

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

運行結(jié)果:

---test1---g_num=1909909

---test2---g_num=2000000

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

可以看到最后的結(jié)果氯哮,加入互斥鎖后,其結(jié)果與預期相符商佛。

上鎖解鎖過程:

????當一個線程調(diào)用鎖的acquire()方法獲得鎖時喉钢,鎖就進入“l(fā)ocked”狀態(tài)。

????每次只有一個線程可以獲得鎖良姆。如果此時另一個線程試圖獲得這個鎖肠虽,該線程就會變?yōu)椤癰locked”狀態(tài),稱為“阻塞”玛追,直到擁有鎖的線程調(diào)用鎖的release()方法釋放鎖之后税课,鎖進入“unlocked”狀態(tài)。

????線程調(diào)度程序從處于同步阻塞狀態(tài)的線程中選擇一個來獲得鎖痊剖,并使得該線程進入運行(running)狀態(tài)韩玩。

7.死鎖

在線程間共享多個資源的時候,如果兩個線程分別占有一部分資源并且同時等待對方的資源陆馁,就會造成死鎖找颓。

盡管死鎖很少發(fā)生,但一旦發(fā)生就會造成應用的停止響應叮贩。下面看一個死鎖的例子

import????threading 击狮,time? ?

printer_mutex = threading.Lock()# 打印機鎖

paper_mutext = threading.Lock()# 紙張鎖

class????ResumeThread(threading.Thread):

"""編寫個人簡歷任務的線程"""

????def????run(self):

????????print("ResumeThread:編寫個人簡歷任務")

????????# 使用打印機資源,先對打印機加鎖

????????printer_mutex.acquire()?

?????????print("--ResumeThread:正在使用打印機資源--")?

?????????time.sleep(1)# 休眠1秒

????????# 使用紙張耗材益老,先對紙張耗材加鎖

????????paper_mutext.acquire()?

????????print("--正在使用紙張資源--")?

?????????time.sleep(1)?

?????????paper_mutext.release()# 釋放紙張鎖

????????# 釋放打印機鎖

????????printer_mutex.release()

class????PaperListThread(threading.Thread):

"""盤點紙張耗材任務的線程"""

????def????run(self):

????????print("PaperListThread:盤點紙張耗材任務")

????????# 使用紙張耗材彪蓬,先對紙張耗材加鎖

????????paper_mutext.acquire()?

?????????print("--PaperListThread:正在盤點紙張耗材--")

?????????time.sleep(1)# 休眠1秒

????????# 使用打印機資源,打印清單

????????printer_mutex.acquire()

?????????print("--正在使用打印機資源--")

?????????time.sleep(1)?

?????????printer_mutex.release()# 釋放打印機鎖

????????# 釋放紙張耗材鎖

????????paper_mutext.release()

if__name__ =='__main__':

? ? ?t1 = ResumeThread()

?????t2 = PaperListThread()

?????t1.start()?

?????t2.start()

從運行結(jié)果可以看出捺萌,兩個線程都在等待對方釋放資源档冬,造成了死鎖。

避免死鎖的方式:

????1.程序設計時要盡量避免

? ? 2.添加超時時間等


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末互婿,一起剝皮案震驚了整個濱河市捣郊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌慈参,老刑警劉巖呛牲,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異驮配,居然都是意外死亡娘扩,警方通過查閱死者的電腦和手機着茸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來琐旁,“玉大人涮阔,你說我怎么就攤上這事』遗梗” “怎么了敬特?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長牺陶。 經(jīng)常有香客問我伟阔,道長,這世上最難降的妖魔是什么掰伸? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任皱炉,我火速辦了婚禮,結(jié)果婚禮上狮鸭,老公的妹妹穿的比我還像新娘合搅。我一直安慰自己,他們只是感情好歧蕉,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布灾部。 她就那樣靜靜地躺著,像睡著了一般廊谓。 火紅的嫁衣襯著肌膚如雪梳猪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天蒸痹,我揣著相機與錄音春弥,去河邊找鬼。 笑死叠荠,一個胖子當著我的面吹牛悔常,可吹牛的內(nèi)容都是我干的蚁飒。 我是一名探鬼主播撕予,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼兢卵,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了者娱?” 一聲冷哼從身側(cè)響起抡笼,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎黄鳍,沒想到半個月后推姻,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡框沟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年藏古,在試婚紗的時候發(fā)現(xiàn)自己被綠了增炭。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡拧晕,死狀恐怖隙姿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情厂捞,我是刑警寧澤输玷,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站蔫敲,受9級特大地震影響饲嗽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜奈嘿,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吞加。 院中可真熱鬧裙犹,春花似錦、人聲如沸衔憨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽践图。三九已至掺冠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間码党,已是汗流浹背德崭。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留揖盘,地道東北人眉厨。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像兽狭,于是被迫代替她去往敵國和親憾股。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

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

  • 1.進程和線程 隊列:1箕慧、進程之間的通信: q = multiprocessing.Queue()2服球、...
    一只寫程序的猿閱讀 1,103評論 0 17
  • 線程 1.同步概念 1.多線程開發(fā)可能遇到的問題 同步不是一起的意思,是協(xié)同步調(diào) 假設兩個線程t1和t2都要對nu...
    TENG書閱讀 606評論 0 1
  • 一文讀懂Python多線程 1颠焦、線程和進程 計算機的核心是CPU斩熊,它承擔了所有的計算任務。它就像一座工廠蒸健,時刻在運...
    星丶雲(yún)閱讀 1,448評論 0 4
  • 多任務可以由多進程完成座享,也可以由一個進程內(nèi)的多線程完成婉商。我們前面提到了進程是由若干線程組成的,一個進程至少有一個線...
    壁花燒年閱讀 812評論 0 0
  • 今天我所要講的是渣叛,有點明白為什么世界上總有千千萬萬的微信名或QQ名叫做“明天會更好”了丈秩,不信你去輸入,不上千也成百...
    754ecf587f5f閱讀 289評論 0 1