您好,登錄后才能下訂單哦!
本文實(shí)例講述了Python多線程原理與用法。分享給大家供大家參考,具體如下:
先來看個(gè)栗子:
下面來看一下I/O秘籍型的線程,舉個(gè)栗子——爬蟲,下面是爬下來的圖片用4個(gè)線程去寫文件
#!/usr/bin/env python # -*- coding:utf-8 -*- import re import urllib import threading import Queue import timeit def getHtml(url): html_page = urllib.urlopen(url).read() return html_page # 提取網(wǎng)頁中圖片的URL def getUrl(html): pattern = r'src="(http://img.*?)"' # 正則表達(dá)式 imgre = re.compile(pattern) imglist = re.findall(imgre, html) # re.findall(pattern,string) 在string中尋找所有匹配成功的字符串,以列表形式返回值 return imglist class getImg(threading.Thread): def __init__(self, queue, thread_name=0): # 線程公用一個(gè)隊(duì)列 threading.Thread.__init__(self) self.queue = queue self.thread_name = thread_name self.start() # 啟動(dòng)線程 # 使用隊(duì)列實(shí)現(xiàn)進(jìn)程間通信 def run(self): global count while (True): imgurl = self.queue.get() # 調(diào)用隊(duì)列對(duì)象的get()方法從隊(duì)頭刪除并返回一個(gè)項(xiàng)目 urllib.urlretrieve(imgurl, 'E:\mnt\girls\%s.jpg' % count) count += 1 if self.queue.empty(): break self.queue.task_done() # 當(dāng)使用者線程調(diào)用 task_done() 以表示檢索了該項(xiàng)目、并完成了所有的工作時(shí),那么未完成的任務(wù)的總數(shù)就會(huì)減少。 imglist = [] def main(): global imglist url = "http://huaban.com/favorite/beauty/" # 要爬的網(wǎng)頁地址 html = getHtml(url) imglist = getUrl(html) def main_1(): global count threads = [] count = 0 queue = Queue.Queue() # 將所有任務(wù)加入隊(duì)列 for img in imglist: queue.put(img) # 多線程爬去圖片 for i in range(4): thread = getImg(queue, i) threads.append(thread) # 阻塞線程,直到線程執(zhí)行完成 for thread in threads: thread.join() if __name__ == '__main__': main() t = timeit.Timer(main_1) print t.timeit(1)
4個(gè)線程的執(zhí)行耗時(shí)為:0.421320716723秒
修改一下main_1換成單線程的:
def main_1(): global count threads = [] count = 0 queue = Queue.Queue() # 將所有任務(wù)加入隊(duì)列 for img in imglist: queue.put(img) # 多線程爬去圖片 for i in range(1): thread = getImg(queue, i) threads.append(thread) # 阻塞線程,直到線程執(zhí)行完成 for thread in threads: thread.join()
單線程的執(zhí)行耗時(shí)為:1.35626623274秒
再來看一個(gè):
#!/usr/bin/env python # -*- coding:utf-8 -*- import threading import timeit def countdown(n): while n > 0: n -= 1 def task1(): COUNT = 100000000 thread1 = threading.Thread(target=countdown, args=(COUNT,)) thread1.start() thread1.join() def task2(): COUNT = 100000000 thread1 = threading.Thread(target=countdown, args=(COUNT // 2,)) thread2 = threading.Thread(target=countdown, args=(COUNT // 2,)) thread1.start() thread2.start() thread1.join() thread2.join() if __name__ == '__main__': t1 = timeit.Timer(task1) print "countdown in one thread ", t1.timeit(1) t2 = timeit.Timer(task2) print "countdown in two thread ", t2.timeit(1)
task1是單線程,task2是雙線程,在我的4核的機(jī)器上的執(zhí)行結(jié)果:
countdown in one thread 3.59939150155
countdown in two thread 9.87704289712
天吶,雙線程比單線程計(jì)算慢了2倍多,這是為什么呢,因?yàn)閏ountdown是CPU密集型任務(wù)(計(jì)算嘛)
I/O密集型任務(wù):線程做I/O處理的時(shí)候會(huì)釋放GIL,其他線程獲得GIL,當(dāng)該線程再做I/O操作時(shí),又會(huì)釋放GIL,如此往復(fù);
CPU密集型任務(wù):在多核多線程比單核多線程更差,原因是單核多線程,每次釋放GIL,喚醒的哪個(gè)線程都能獲取到GIL鎖,所以能夠無縫執(zhí)行(單核多線程的本質(zhì)就是順序執(zhí)行),但多核,CPU0釋放GIL后,其他CPU上的線程都會(huì)進(jìn)行競(jìng)爭(zhēng),但GIL可能會(huì)馬上又被CPU0(CPU0上可能不止一個(gè)線程)拿到,導(dǎo)致其他幾個(gè)CPU上被喚醒后的線程會(huì)醒著等待到切換時(shí)間后又進(jìn)入待調(diào)度狀態(tài),這樣會(huì)造成線程顛簸(thrashing),導(dǎo)致效率更低。
更多關(guān)于Python相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Python進(jìn)程與線程操作技巧總結(jié)》、《Python數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Python函數(shù)使用技巧總結(jié)》、《Python字符串操作技巧匯總》、《Python入門與進(jìn)階經(jīng)典教程》、《Python+MySQL數(shù)據(jù)庫程序設(shè)計(jì)入門教程》及《Python常見數(shù)據(jù)庫操作技巧匯總》
希望本文所述對(duì)大家Python程序設(shè)計(jì)有所幫助。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。