国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Python > 正文

Python用threading實(shí)現(xiàn)多線程詳解

2019-11-25 16:22:41
字體:
供稿:網(wǎng)友

多線程

多線程是個(gè)提高程序運(yùn)行效率的好辦法,本來要順序執(zhí)行的程序現(xiàn)在可以并行執(zhí)行,可想而知效率要提高很多。但是多線程也不是能提高所有程序的效率。程序的兩個(gè)極端是‘CPU 密集型'和‘I/O 密集型'兩種,多線程技術(shù)比較適用于后者,因?yàn)樵诖薪Y(jié)構(gòu)中當(dāng)你去讀寫磁盤或者網(wǎng)絡(luò)通信的時(shí)候 CPU 是閑著的,畢竟網(wǎng)絡(luò)比磁盤要慢幾個(gè)數(shù)量級,磁盤比內(nèi)存慢幾個(gè)數(shù)量級,內(nèi)存又比 CPU 慢幾個(gè)數(shù)量級。多線程技術(shù)就可以同時(shí)執(zhí)行,比如你的程序需要發(fā)送 N 個(gè) http 數(shù)據(jù)包(10 秒),還需要將文件從一個(gè)位置復(fù)制到另一個(gè)位置(20 秒),然后還需要統(tǒng)計(jì)另一個(gè)文件中'hello,world'字符串的出現(xiàn)次數(shù)(4 秒),現(xiàn)在一共是要用 34 秒。但是因?yàn)檫@些操作之間沒有關(guān)聯(lián),所以可以寫成多線程程序,幾乎只需要 20 秒就完成了。這是針對 I/O 密集型的,如果是 CPU 密集型的就不行了。比如我的程序要計(jì)算 1000 的階乘(10 秒),還要計(jì)算 100000 的累加(5 秒),那么即使程序是并行的,還是會(huì)要用 15 秒,甚至更多。因?yàn)楫?dāng)程序使用 CPU 的時(shí)候 CPU 是通過輪轉(zhuǎn)來執(zhí)行的,IO 密集型的程序可以在 IO 的同時(shí)用 CPU 計(jì)算,但是這里的 CPU 密集型就只能先執(zhí)行一會(huì)兒線程 1 再執(zhí)行一會(huì)兒線程 2。所以就需要 15 秒,甚至?xí)啵驗(yàn)?CPU 在切換的時(shí)候需要耗時(shí)。解決 CPU 密集型程序的多線程問題就是 CPU 的事情了,比如 Intel 的超線程技術(shù),可以在同一個(gè)核心上真正的并行兩個(gè)線程,所以稱之為‘雙核四線程'或者‘四核八線程',我們這里具體的先不談,談我也不知道。

Python 騙人

說了這么多多線程的好處,但是其實(shí) Python 不支持真正意義上的多線程編程。在 Python 中有一個(gè)叫做 GIL 的東西,中文是 全局解釋器 ,這東西控制了 Python,讓 Python 只能同時(shí)運(yùn)行一個(gè)線程。相當(dāng)于說真正意義上的多線程是由 CPU 來控制的,Python 中的多線程由 GIL 控制。如果有一個(gè) CPU 密集型程序,用 C 語言寫的,運(yùn)行在一個(gè)四核處理器上,采用多線程技術(shù)的話最多可以獲得 4 倍的效率提升,但是如果用 Python 寫的話并不會(huì)有提高,甚至?xí)兟?,因?yàn)榫€程切換的問題。所以 Python 多線程相對更加適合寫 I/O 密集型程序,再說了真正的對效率要求很高的 CPU 密集型程序都用 C/C++ 去了。

第一個(gè)多線程

Python 中多線程的庫一般用thread和threading這兩個(gè),thread不推薦新手和一般人使用,threading模塊就相當(dāng)夠用了。

有一個(gè)程序,如下。兩個(gè)循環(huán),分別休眠 3 秒和 5 秒,串行執(zhí)行的話需要 8 秒。

#!/usr/bin/env python# coding=utf-8import timedef sleep_3(): time.sleep(3)def sleep_5(): time.sleep(5)if __name__ == '__main__': start_time = time.time() print 'start sleep 3' sleep_3() print 'start sleep 5' sleep_5() end_time = time.time() print str(end_time - start_time) + ' s'

輸出是這樣的

start sleep 3start sleep 58.00100016594 s

然后我們對它進(jìn)行修改,使其變成多線程程序,雖然改動(dòng)沒有幾行。首先引入了 threading 的庫,然后實(shí)例化一個(gè) threading.Thread 對象,將一個(gè)函數(shù)傳進(jìn)構(gòu)造方法就行了。然后調(diào)用 Thread 的 start 方法開始一個(gè)線程。join() 方法可以等待該線程結(jié)束,就像我下面用的,如果我不加那兩個(gè)等待線程結(jié)束的代碼,那么就會(huì)直接執(zhí)行輸出時(shí)間的語句,這樣一來統(tǒng)計(jì)的時(shí)間就不對了。

#!/usr/bin/env python# coding=utf-8import timeimport threading # 引入threadingdef sleep_3(): time.sleep(3)def sleep_5(): time.sleep(5)if __name__ == '__main__': start_time = time.time() print 'start sleep 3' thread_1 = threading.Thread(target=sleep_3)  # 實(shí)例化一個(gè)線程對象,使線程執(zhí)行這個(gè)函數(shù) thread_1.start()  # 啟動(dòng)這個(gè)線程 print 'start sleep 5' thread_2 = threading.Thread(target=sleep_5)  # 實(shí)例化一個(gè)線程對象,使線程執(zhí)行這個(gè)函數(shù) thread_2.start()  # 啟動(dòng)這個(gè)線程 thread_1.join()  # 等待thread_1結(jié)束 thread_2.join()  # 等待thread_2結(jié)束 end_time = time.time() print str(end_time - start_time) + ' s'

執(zhí)行結(jié)果是這樣的

start sleep 3start sleep 55.00099992752 s

daemon 守護(hù)線程

在我們理解中守護(hù)線程應(yīng)該是很重要的,類比于 Linux 中的守護(hù)進(jìn)程。但是在threading.Thread中偏偏不是。

如果把一個(gè)線程設(shè)置為守護(hù)線程,就表示這個(gè)線程是不重要的,進(jìn)程退出的時(shí)候不需要等待這個(gè)線程執(zhí)行完成。 ---------《Python 核心編程 第三版》

在 Thread 對象中默認(rèn)所有線程都是非守護(hù)線程,這里有兩個(gè)例子說明區(qū)別。這段代碼執(zhí)行的時(shí)候就沒指定my_thread的daemon屬性,所以默認(rèn)為非守護(hù),所以進(jìn)程等待他結(jié)束。最后就可以看到 100 個(gè) hello,world

#!/usr/bin/env python# coding=utf-8import threadingdef hello_world(): for i in range(100):  print 'hello,world'if __name__ == '__main__': my_thread = threading.Thread(target=hello_world) my_thread.start()

這里設(shè)置了my_thread為守護(hù)線程,所以進(jìn)程直接就退出了,并沒有等待他的結(jié)束,所以我們看不到 100 個(gè) hello,world 只有幾個(gè)而已。甚至還會(huì)拋出一個(gè)異常告訴我們有線程沒結(jié)束。

#!/usr/bin/env python# coding=utf-8import threadingdef hello_world(): for i in range(100):  print 'hello,world'if __name__ == '__main__': my_thread = threading.Thread(target=hello_world) my_thread.daemon = True # 設(shè)置了標(biāo)志位True my_thread.start()

傳個(gè)參數(shù)

之前的代碼都是直接執(zhí)行一段代碼,沒有過參數(shù)的傳遞,那么怎么傳遞參數(shù)呢?其實(shí)還是很簡單的。threading.Thread(target=hello_world, args=('hello,', 'world'))就可以了。args 后面跟的是一個(gè)元組,如果沒有參數(shù)可以不寫,如果有參數(shù)就直接在元組里按順序添加就行了。

#!/usr/bin/env python# coding=utf-8import threadingdef hello_world(str_1, str_2): for i in range(10):  print str_1 + str_2if __name__ == '__main__': my_thread = threading.Thread(target=hello_world, args=('hello,', 'world')) # 這里傳遞參數(shù) my_thread.start()

再來個(gè)多線程

threading 有三種創(chuàng)建 Thread 對象的方式,但是一般只會(huì)用到兩種,一種是上面0X02說的傳個(gè)函數(shù)進(jìn)去,另一種就是這里說的繼承threading.Thread。在這兒我們自己定義了兩個(gè)類,類里重寫了 run() 方法,也就是調(diào)用 start() 之后執(zhí)行的代碼,開啟線程就和之前開啟是一樣的。之前的方式更面向過程,這個(gè)更面向?qū)ο蟆?br />

#!/usr/bin/env python# coding=utf-8import threadingclass MyThreadHello(threading.Thread): def run(self):  for i in range(100):   print 'hello'class MyThreadWorld(threading.Thread): def run(self):  for i in range(100):   print 'world'if __name__ == '__main__': thread_hello = MyThreadHello() thread_world = MyThreadWorld() thread_hello.start() thread_world.start()

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 阿城市| 德州市| 沂源县| 通州市| 安义县| 玉溪市| 田东县| 伊吾县| 磐安县| 田东县| 仙桃市| 宁南县| 时尚| 西乡县| 昌平区| 楚雄市| 治县。| 西乡县| 微博| 凤庆县| 东兰县| 肇源县| 建湖县| 揭东县| 丹巴县| 玉门市| 陵川县| 中超| 肥乡县| 岳池县| 罗源县| 九江市| 天水市| 长治县| 清涧县| 永靖县| 长寿区| 加查县| 荆州市| 射洪县| 象州县|