摘要:對于操作為主的編程來說,多進(jìn)程和多先產(chǎn)出的性能差別不大,甚至多線程比多進(jìn)程的性能還高,因?yàn)槎嗑€程編程更加輕量級。
GIL
global interpreter lock(cpython)
同一時(shí)刻只有一個(gè)線程運(yùn)行在一個(gè)cpu上執(zhí)行字節(jié)碼(無法將多個(gè)線程映射到多個(gè)cpu上)
import dis def add(a): a = a + 1 return a print(dis.dis(add))GIL在某些情況下會釋放
每次的結(jié)果都不一樣 線程之間的安全問題
GIL會根據(jù)執(zhí)行的直接碼行數(shù)或者時(shí)間片釋放GIL
遇到IO操作時(shí)主動釋放
total = 0 def add(): #1. dosomething1 #2. io操作 # 1. dosomething3 global total for i in range(1000000): total += 1 def desc(): global total for i in range(1000000): total -= 1 import threading thread1 = threading.Thread(target=add) thread2 = threading.Thread(target=desc) thread1.start() thread2.start() thread1.join() thread2.join() print(total)多線程編程
操作系統(tǒng)能夠調(diào)度的的最小單位是進(jìn)程,因?yàn)檫M(jìn)程對系統(tǒng)的資源消耗非常大,所以后期就演變成了線程,線程實(shí)際上是依賴于我們的進(jìn)程(任務(wù)管理器中我們實(shí)際上能看到的其實(shí)是進(jìn)程 ),操作系統(tǒng)能調(diào)度的最小單元是線程。
對于io操作為主的編程來說,多進(jìn)程和多先產(chǎn)出的性能差別不大,甚至多線程比多進(jìn)程的性能還高,因?yàn)槎嗑€程編程更加輕量級。
簡單的線程import time from threading import Thread def get_detail_html(url): print("get detail html started") time.sleep(2) print("get detail html end") def get_detail_url(url): print("get detail url started") time.sleep(4) print("get detail url end") if __name__ == "__main__": thread1 = Thread(target=get_detail_html, args=("",)) thread2 = Thread(target=get_detail_url, args=("",)) # 設(shè)置為守護(hù)線程 當(dāng)主線程運(yùn)行完時(shí) 子線程被kill掉 thread1.setDaemon(True) thread2.setDaemon(True) start_time = time.time() thread1.start() thread2.start() # 設(shè)置為阻塞 等待線程運(yùn)行完再關(guān)閉主線程 thread1.join() thread2.join() # 默認(rèn)情況下 主線程退出與時(shí) 子線程不會被kill掉 print("last time: {}".format(time.time() - start_time))重載線程實(shí)現(xiàn)多線程
import time import threading def get_detail_html(url): print("get detail html started") time.sleep(2) print("get detail html end") def get_detail_url(url): print("get detail url started") time.sleep(4) print("get detail url end") #2. 通過集成Thread來實(shí)現(xiàn)多線程 class GetDetailHtml(threading.Thread): def __init__(self, name): super().__init__(name=name) def run(self): print("get detail html started") time.sleep(2) print("get detail html end") class GetDetailUrl(threading.Thread): def __init__(self, name): super().__init__(name=name) def run(self): print("get detail url started") time.sleep(4) print("get detail url end") if __name__ == "__main__": thread1 = GetDetailHtml("get_detail_html") thread2 = GetDetailUrl("get_detail_url") start_time = time.time() thread1.start() thread2.start() thread1.join() thread2.join() #當(dāng)主線程退出的時(shí)候, 子線程kill掉 print ("last time: {}".format(time.time()-start_time))多線程之間的通信
使用queue
# filename: thread_queue_test.py # 通過queue的方式進(jìn)行線程間同步 from queue import Queue import time import threading def get_detail_html(queue): # 死循環(huán) 爬取文章詳情頁 while True: url = queue.get() # for url in detail_url_list: print("get detail html started") time.sleep(2) print("get detail html end") def get_detail_url(queue): # 死循環(huán) 爬取文章列表頁 while True: print("get detail url started") time.sleep(4) for i in range(20): # put 等到有空閑位置 再放入 # put_nowait 非阻塞方式 queue.put("http://projectsedu.com/{id}".format(id=i)) print("get detail url end") # 1. 線程通信方式- 共享變量 if __name__ == "__main__": detail_url_queue = Queue(maxsize=1000) thread_detail_url = threading.Thread(target=get_detail_url, args=(detail_url_queue,)) for i in range(10): html_thread = threading.Thread(target=get_detail_html, args=(detail_url_queue,)) html_thread.start() start_time = time.time() # 調(diào)用task_down從主線程退出 detail_url_queue.task_done() # 從queue的角度阻塞 detail_url_queue.join() print("last time: {}".format(time.time() - start_time))線程的同步問題
在多線程編程中必須要面對的問題
無鎖不安全的原因# 沒有鎖 def add1(a): a += 1 def desc1(a): a -= 1 """add 1. load a a = 0 2. load 1 1 3. + 1 4. 賦值給a a=1 """ """add 1. load a a = 0 2. load 1 1 3. - 1 4. 賦值給a a=-1 """ import dis print(dis.dis(add1)) print(dis.dis(desc1))普通鎖(Lock)
用鎖會影響性能,鎖會引起死鎖(兩次獲取鎖,獲取鎖之后不釋放,互相等待(a需要b的資源 b需要a的資源))
import threading from threading import Lock total = 0 # 定義一把鎖 lock = Lock() def add(): global total global lock for i in range(1000000): # 獲取鎖 lock.acquire() total += 1 # 釋放鎖 lock.release() def desc(): global total for i in range(1000000): lock.acquire() total -= 1 lock.release() thread1 = threading.Thread(target=add) thread2 = threading.Thread(target=desc) thread1.start() thread2.start() thread1.join() thread2.join() print(total)相互等待(資源競爭)
""" A(a、b) acquire (a) acquire (b) B(a、b) acquire (b) acquire (a) # 解決辦法 B(a、b) acquire (a) acquire (b) """可重入鎖(Rlock)
import threading from threading import RLock total = 0 # 可重入鎖 可以在同一個(gè)線程中可載入多次 lock = RLock() def add(lock): global total for i in range(1000000): # 獲取鎖 lock.acquire() lock.acquire() total += 1 do_something(lock) # 釋放鎖 lock.release() lock.release() def desc(): global total for i in range(1000000): lock.acquire() total -= 1 lock.release() def do_something(lock): lock.acquire() # do something lock.release() thread1 = threading.Thread(target=add) thread2 = threading.Thread(target=desc) thread1.start() thread2.start() thread1.join() thread2.join() print(total)條件變量鎖(condition)
用于復(fù)雜的線程間同步
# 沒有條件鎖 不能實(shí)現(xiàn)對話 import threading class XiaoAi(threading.Thread): def __init__(self, lock): super().__init__(name="小愛") self.lock = lock def run(self): self.lock.acquire() print("{} : 在 ".format(self.name)) self.lock.release() self.lock.acquire() print("{} : 好啊 ".format(self.name)) self.lock.release() class TianMao(threading.Thread): def __init__(self, lock): super().__init__(name="天貓精靈") self.lock = lock def run(self): self.lock.acquire() print("{} : 小愛同學(xué) ".format(self.name)) self.lock.release() self.lock.acquire() print("{} : 我們來對古詩吧 ".format(self.name)) self.lock.release() if __name__ == "__main__": cond = threading.Condition() xiaoai = XiaoAi(cond) tianmao = TianMao(cond) xiaoai.start() tianmao.start()
# 條件鎖 import threading class XiaoAi(threading.Thread): def __init__(self, cond): super().__init__(name="小愛") self.cond = cond def run(self): with self.cond: self.cond.wait() print("{} : 在 ".format(self.name)) self.cond.notify() self.cond.wait() print("{} : 好啊 ".format(self.name)) self.cond.notify() self.cond.wait() print("{} : 君住長江尾 ".format(self.name)) self.cond.notify() self.cond.wait() print("{} : 共飲長江水 ".format(self.name)) self.cond.notify() self.cond.wait() print("{} : 此恨何時(shí)已 ".format(self.name)) self.cond.notify() self.cond.wait() print("{} : 定不負(fù)相思意 ".format(self.name)) self.cond.notify() class TianMao(threading.Thread): def __init__(self, cond): super().__init__(name="天貓精靈") self.cond = cond def run(self): with self.cond: print("{} : 小愛同學(xué) ".format(self.name)) self.cond.notify() self.cond.wait() print("{} : 我們來對古詩吧 ".format(self.name)) self.cond.notify() self.cond.wait() print("{} : 我住長江頭 ".format(self.name)) self.cond.notify() self.cond.wait() print("{} : 日日思君不見君 ".format(self.name)) self.cond.notify() self.cond.wait() print("{} : 此水幾時(shí)休 ".format(self.name)) self.cond.notify() self.cond.wait() print("{} : 只愿君心似我心 ".format(self.name)) self.cond.notify() self.cond.wait() if __name__ == "__main__": from concurrent import futures cond = threading.Condition() xiaoai = XiaoAi(cond) tianmao = TianMao(cond) # 啟動順序很重要 # 在調(diào)用with cond之后才能調(diào)用wait或者notify方法 # condition有兩層鎖, 一把底層鎖會在線程調(diào)用了wait方法的時(shí)候釋放, # 上面的鎖會在每次調(diào)用wait的時(shí)候分配一把并放入到cond的等待隊(duì)列中, # 等到notify方法的喚醒 xiaoai.start() tianmao.start()
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/42970.html
摘要:進(jìn)程可創(chuàng)建多個(gè)線程來執(zhí)行同一程序的不同部分。就緒等待線程調(diào)度。運(yùn)行線程正常運(yùn)行阻塞暫停運(yùn)行,解除阻塞后進(jìn)入狀態(tài)重新等待調(diào)度。消亡線程方法執(zhí)行完畢返回或者異常終止。多線程多的情況下,依次執(zhí)行各線程的方法,前頭一個(gè)結(jié)束了才能執(zhí)行后面一個(gè)。 淺談Python多線程 作者簡介: 姓名:黃志成(小黃)博客: 博客 線程 一.什么是線程? 操作系統(tǒng)原理相關(guān)的書,基本都會提到一句很經(jīng)典的話: 進(jìn)程...
摘要:多線程的理解多進(jìn)程和多線程都可以執(zhí)行多個(gè)任務(wù),線程是進(jìn)程的一部分。多線程創(chuàng)建在中,同樣可以實(shí)現(xiàn)多線程,有兩個(gè)標(biāo)準(zhǔn)模塊和,不過我們主要使用更高級的模塊。多線程的應(yīng)用場景。 1、多線程的理解 多進(jìn)程和多線程都可以執(zhí)行多個(gè)任務(wù),線程是進(jìn)程的一部分。線程的特點(diǎn)是線程之間可以共享內(nèi)存和變量,資源消耗少(不過在Unix環(huán)境中,多進(jìn)程和多線程資源調(diào)度消耗差距不明顯,Unix調(diào)度較快),缺點(diǎn)是線程之間...
摘要:其次,解釋器的主循環(huán),一個(gè)名為的函數(shù),讀取字節(jié)碼并逐個(gè)執(zhí)行其中的指令。所有線程都運(yùn)行相同的代碼,并以相同的方式定期從它們獲取鎖定。無論如何,其他線程無法并行運(yùn)行。 概述 如今我也是使用Python寫代碼好多年了,但是我卻很少關(guān)心GIL的內(nèi)部機(jī)制,導(dǎo)致在寫Python多線程程序的時(shí)候。今天我們就來看看CPython的源代碼,探索一下GIL的源碼,了解為什么Python里要存在這個(gè)GIL,...
摘要:上一篇文章進(jìn)程專題完結(jié)篇多進(jìn)程處理的一般建議下一篇文章線程專題多線程使用的必要性進(jìn)程線程進(jìn)程能夠完成多任務(wù),比如在一個(gè)電腦上可以運(yùn)行多個(gè)軟件。由于占用資源少,也使得多線程程序并發(fā)比較高。 上一篇文章:Python進(jìn)程專題完結(jié)篇:多進(jìn)程處理的一般建議下一篇文章:Python線程專題1:多線程使用的必要性 進(jìn)程VS線程 進(jìn)程:能夠完成多任務(wù),比如在一個(gè)電腦上可以運(yùn)行多個(gè)軟件。線程:也能夠...
摘要:也提供多線程支持,而且中的線程并非是模擬出來的多線程,而是系統(tǒng)級別的標(biāo)準(zhǔn)庫提供了兩個(gè)模塊和。同一個(gè)變量,線程則會互相共享。例如多個(gè)線程對銀行中的某一個(gè)賬戶進(jìn)行操作。但是實(shí)際情況是隨意切換線程。說到的多線程編程,就會繞不過。 該文章參考了http://www.liaoxuefeng.com/wi... 廖雪峰的教程。 一個(gè)進(jìn)程至少有一個(gè)線程。Python也提供多線程支持,而且Python...
摘要:多線程和鎖作者博客進(jìn)程和線程進(jìn)程是執(zhí)行中的計(jì)算機(jī)程序。線程包括開始執(zhí)行順序和結(jié)束三部分。的多進(jìn)程相關(guān)模塊模塊是高級別的多線程模塊。線程鎖當(dāng)多線程爭奪鎖時(shí),允許第一個(gè)獲得鎖的線程進(jìn)入臨街區(qū),并執(zhí)行代碼。 Python 多線程和鎖 作者博客:http://zzir.cn/ 進(jìn)程和線程 進(jìn)程是執(zhí)行中的計(jì)算機(jī)程序。每個(gè)進(jìn)程都擁有自己的地址空間、內(nèi)存、數(shù)據(jù)棧及其它的輔助數(shù)據(jù)。操作系統(tǒng)管理著所有的...
閱讀 4021·2021-11-22 13:53
閱讀 3617·2021-11-19 11:29
閱讀 1265·2021-09-08 09:35
閱讀 3162·2020-12-03 17:26
閱讀 514·2019-08-29 16:06
閱讀 2106·2019-08-26 13:50
閱讀 1180·2019-08-23 18:32
閱讀 2153·2019-08-23 18:12