摘要:某進程內的線程在其它進程不可見。線程的實體包括程序數據和。包括以下信息線程狀態。當線程不運行時,被保存的現場資源。用戶級線程執行系統調用指令時將導致其所屬進程被中斷,而內核支持線程執行系統調用指令時,只導致該線程被中斷。線程能夠利用的表空
操作系統線程理論 線程概念的引入背景
進程
之前我們已經了解了操作系統中進程的概念,程序并不能多帶帶運行,只有將程序裝載到內存中,系統為它分配資源才能運行,而這種執行的程序就稱之為進程。程序和進程的區別就在于:程序是指令的集合,它是進程運行的靜態描述文本;進程是程序的一次執行活動,屬于動態概念。在多道編程中,我們允許多個程序同時加載到內存中,在操作系統的調度下,可以實現并發地執行。這是這樣的設計,大大提高了CPU的利用率。進程的出現讓每個用戶感覺到自己獨享CPU,因此,進程就是為了在CPU上實現多道編程而提出的。
有了進程為什么要有線程
進程有很多優點,它提供了多道編程,讓我們感覺我們每個人都擁有自己的CPU和其他資源,可以提高計算機的利用率。很多人就不理解了,既然進程這么優秀,為什么還要線程呢?其實,仔細觀察就會發現進程還是有很多缺陷的,主要體現在兩點上:
進程只能在一個時間干一件事,如果想同時干兩件事或多件事,進程就無能為力了。
進程在執行的過程中如果阻塞,例如等待輸入,整個進程就會掛起,即使進程中有些工作不依賴于輸入的數據,也將無法執行。
如果這兩個缺點理解比較困難的話,舉個現實的例子也許你就清楚了:如果把我們上課的過程看成一個進程的話,那么我們要做的是耳朵聽老師講課,手上還要記筆記,腦子還要思考問題,這樣才能高效的完成聽課的任務。而如果只提供進程這個機制的話,上面這三件事將不能同時執行,同一時間只能做一件事,聽的時候就不能記筆記,也不能用腦子思考,這是其一;如果老師在黑板上寫演算過程,我們開始記筆記,而老師突然有一步推不下去了,阻塞住了,他在那邊思考著,而我們呢,也不能干其他事,即使你想趁此時思考一下剛才沒聽懂的一個問題都不行,這是其二。
現在你應該明白了進程的缺陷了,而解決的辦法很簡單,我們完全可以讓聽、寫、思三個獨立的過程,并行起來,這樣很明顯可以提高聽課的效率。而實際的操作系統中,也同樣引入了這種類似的機制——線程。
線程的出現
60年代,在OS中能擁有資源和獨立運行的基本單位是進程,然而隨著計算機技術的發展,進程出現了很多弊端,一是由于進程是資源擁有者,創建、撤消與切換存在較大的時空開銷,因此需要引入輕型進程;二是由于對稱多處理機(SMP)出現,可以滿足多個運行單位,而多個進程并行開銷過大。
因此在80年代,出現了能獨立運行的基本單位——線程(Threads)。
注意:進程是資源分配的最小單位,線程是CPU調度的最小單位.
每一個進程中至少有一個線程。
線程與進程的區別可以歸納為以下4點:
1)地址空間和其它資源(如打開文件):進程間相互獨立,同一進程的各線程間共享。某進程內的線程在其它進程不可見。
2)通信:進程間通信IPC,線程間可以直接讀寫進程數據段(如全局變量)來進行通信——需要進程同步和互斥手段的輔助,以保證數據的一致性。
3)調度和切換:線程上下文切換比進程上下文切換要快得多。
4)在多線程操作系統中,進程不是一個可執行的實體。
*通過漫畫了解線程進城
1)輕型實體
線程中的實體基本上不擁有系統資源,只是有一點必不可少的、能保證獨立運行的資源。
線程的實體包括程序、數據和TCB。線程是動態概念,它的動態特性由線程控制塊TCB(Thread Control Block)描述。
TCB包括以下信息:
(1)線程狀態。
(2)當線程不運行時,被保存的現場資源。
(3)一組執行堆棧。
(4)存放每個線程的局部變量主存區。
(5)訪問同一個進程中的主存和其它資源。
用于指示被執行指令序列的程序計數器、保留局部變量、少數狀態參數和返回地址等的一組寄存器和堆棧。
復制代碼
2)獨立調度和分派的基本單位。
在多線程OS中,線程是能獨立運行的基本單位,因而也是獨立調度和分派的基本單位。由于線程很“輕”,故線程的切換非常迅速且開銷小(在同一進程中的)。
3)共享進程資源。
線程在同一進程中的各個線程,都可以共享該進程所擁有的資源,這首先表現在:所有線程都具有相同的進程id,這意味著,線程可以訪問該進程的每一個內存資源;此外,還可以訪問進程所擁有的已打開文件、定時器、信號量機構等。由于同一個進程內的線程共享內存和文件,所以線程之間互相通信不必調用內核。
4)可并發執行。
在一個進程中的多個線程之間,可以并發執行,甚至允許在一個進程中所有線程都能并發執行;同樣,不同進程中的線程也能并發執行,充分利用和發揮了處理機與外圍設備并行工作的能力。
開啟一個字處理軟件進程,該進程肯定需要辦不止一件事情,比如監聽鍵盤輸入,處理文字,定時自動將文字保存到硬盤,這三個任務操作的都是同一塊數據,因而不能用多進程。只能在一個進程里并發地開啟三個線程,如果是單線程,那就只能是,鍵盤輸入時,不能處理文字和自動保存,自動保存時又不能輸入和處理文字。
內存中的線程多個線程共享同一個進程的地址空間中的資源,是對一臺計算機上多個進程的模擬,有時也稱線程為輕量級的進程。
而對一臺計算機上多個進程,則共享物理內存、磁盤、打印機等其他物理資源。多線程的運行也多進程的運行類似,是cpu在多個線程之間的快速切換。
不同的進程之間是充滿敵意的,彼此是搶占、競爭cpu的關系,如果迅雷會和QQ搶資源。而同一個進程是由一個程序員的程序創建,所以同一進程內的線程是合作關系,一個線程可以訪問另外一個線程的內存地址,大家都是共享的,一個線程干死了另外一個線程的內存,那純屬程序員腦子有問題。
類似于進程,每個線程也有自己的堆棧,不同于進程,線程庫無法利用時鐘中斷強制線程讓出CPU,可以調用thread_yield運行線程自動放棄cpu,讓另外一個線程運行。
線程通常是有益的,但是帶來了不小程序設計難度,線程的問題是:
1. 父進程有多個線程,那么開啟的子線程是否需要同樣多的線程
2. 在同一個進程中,如果一個線程關閉了文件,而另外一個線程正準備往該文件內寫內容呢?
因此,在多線程的代碼中,需要更多的心思來設計程序的邏輯、保護程序的數據。
線程的實現可以分為兩類:用戶級線程(User-Level Thread)和內核線線程(Kernel-Level Thread),后者又稱為內核支持的線程或輕量級進程。在多線程操作系統中,各個系統的實現方式并不相同,在有的系統中實現了用戶級線程,有的系統中實現了內核級線程。
用戶級線程
內核的切換由用戶態程序自己控制內核切換,不需要內核干涉,少了進出內核態的消耗,但不能很好的利用多核Cpu。
內核級線程
內核級線程:切換由內核控制,當線程進行切換的時候,由用戶態轉化為內核態。切換完畢要從內核態返回用戶態;可以很好的利用smp,即利用多核cpu。windows線程就是這樣的。
用戶級與內核級線程的對比
1.用戶級線程和內核級線程的區別
1 內核支持線程是OS內核可感知的,而用戶級線程是OS內核不可感知的。 2 用戶級線程的創建、撤消和調度不需要OS內核的支持,是在語言(如Java)這一級處理的;而內核支持線程的創建、撤消和調度都需OS內核提供支持,而且與進程的創建、撤消和調度大體是相同的。 3 用戶級線程執行系統調用指令時將導致其所屬進程被中斷,而內核支持線程執行系統調用指令時,只導致該線程被中斷。 4 在只有用戶級線程的系統內,CPU調度還是以進程為單位,處于運行狀態的進程中的多個線程,由用戶程序控制線程的輪換運行;在有內核支持線程的系統內,CPU調度則以線程為單位,由OS的線程調度程序負責線程的調度。 5 用戶級線程的程序實體是運行在用戶態下的程序,而內核支持線程的程序實體則是可以運行在任何狀態下的程序。
2.內核線程的優缺點
優點:當有多個處理機時,一個進程的多個線程可以同時執行。 缺點:由內核進行調度。
3.用戶級線程的優缺點
優點: 線程的調度不需要內核直接參與,控制簡單。 可以在不支持線程的操作系統中實現。 創建和銷毀線程、線程切換代價等線程管理的代價比內核線程少得多。 允許每個進程定制自己的調度算法,線程管理比較靈活。 線程能夠利用的表空間和堆棧空間比內核級線程多。 同一進程中只能同時有一個線程在運行,如果有一個線程使用了系統調用而阻塞,那么整個進程都會被掛起。另外,頁面失效也會產生同樣的問題。 缺點: 資源調度按照進程進行,多個處理機下,同一個進程中的線程只能在同一個處理機下分時復用
混合實現
用戶級與內核級的多路復用,內核同一調度內核線程,每個內核線程對應n個用戶線程
全局解釋器鎖GIL
Python代碼的執行由Python虛擬機(也叫解釋器主循環)來控制。Python在設計之初就考慮到要在主循環中,同時只有一個線程在執行。雖然 Python 解釋器中可以“運行”多個線程,但在任意時刻只有一個線程在解釋器中運行。
對Python虛擬機的訪問由全局解釋器鎖(GIL)來控制,正是這個鎖能保證同一時刻只有一個線程在運行。
在多線程環境中,Python 虛擬機按以下方式執行:
a、設置 GIL;
b、切換到一個線程去運行;
c、運行指定數量的字節碼指令或者線程主動讓出控制(可以調用 time.sleep(0));
d、把線程設置為睡眠狀態;
e、解鎖 GIL;
d、再次重復以上所有步驟。
在調用外部代碼(如 C/C++擴展函數)的時候,GIL將會被鎖定,直到這個函數結束為止(由于在這期間沒有Python的字節碼被運行,所以不會做線程切換)編寫擴展的程序員可以主動解鎖GIL。
python線程模塊的選擇
Python提供了幾個用于多線程編程的模塊,包括thread、threading和Queue等。thread和threading模塊允許程序員創建和管理線程。thread模塊提供了基本的線程和鎖的支持,threading提供了更高級別、功能更強的線程管理的功能。Queue模塊允許用戶創建一個可以用于多個線程之間共享數據的隊列數據結構。
避免使用thread模塊,因為更高級別的threading模塊更為先進,對線程的支持更為完善,而且使用thread模塊里的屬性有可能會與threading出現沖突;其次低級別的thread模塊的同步原語很少(實際上只有一個),而threading模塊則有很多;再者,thread模塊中當主線程結束時,所有的線程都會被強制結束掉,沒有警告也不會有正常的清除工作,至少threading模塊能確保重要的子線程退出后進程才退出。
thread模塊不支持守護線程,當主線程退出時,所有的子線程不論它們是否還在工作,都會被強行退出。而threading模塊支持守護線程,守護線程一般是一個等待客戶請求的服務器,如果沒有客戶提出請求它就在那等著,如果設定一個線程為守護線程,就表示這個線程是不重要的,在進程退出的時候,不用等待這個線程退出。
multiprocess模塊的完全模仿了threading模塊的接口,二者在使用層面,有很大的相似性,見官網鏈接:
線程的創建Threading.Thread類
1.線程的創建
創建線程的方式1:
#!/usr/bin/env python # -*- coding:utf-8 -*- from threading import Thread import time def sayhi(name): time.sleep(2) print("%s say hello" %name) if __name__ == "__main__": t=Thread(target=sayhi,args=("egon",)) t.start() print("主線程")
創建線程的方式2:
from threading import Thread import time class Sayhi(Thread): def __init__(self,name): super().__init__() self.name=name def run(self): time.sleep(2) print("%s say hello" % self.name) if __name__ == "__main__": t = Sayhi("egon") t.start() print("主線程")
2.多線程與多進程
pid的比較
from threading import Thread from multiprocessing import Process import os def work(): print("hello",os.getpid()) if __name__ == "__main__": #part1:在主進程下開啟多個線程,每個線程都跟主進程的pid一樣 t1=Thread(target=work) t2=Thread(target=work) t1.start() t2.start() print("主線程/主進程pid",os.getpid()) #part2:開多個進程,每個進程都有不同的pid p1=Process(target=work) p2=Process(target=work) p1.start() p2.start() print("主線程/主進程pid",os.getpid())
開啟效率的較量
import time from multiprocessing import Process from threading import Thread n = 10 def func(i): global n n -= 1 if __name__ == "__main__": start = time.time() t_lst = [] for i in range(100): t = Thread(target=func,args=(i,)) t.start() t_lst.append(t) for t in t_lst:t.join() print("線程 ",time.time() - start) start = time.time() p_lst = [] for i in range(100): p = Process(target=func,args=(i,)) p.start() p_lst.append(p) for p in p_lst: p.join() print("進程 :",time.time() - start)
內存數據的共享問題
from threading import Thread from multiprocessing import Process def work(): global n n=0 print("子線程/子進程",n) if __name__ == "__main__": n=100 p=Process(target=work) p.start() p.join() print("主",n) #毫無疑問子進程p已經將自己的全局的n改成了0,但改的僅僅是它自己的,查看父進程的n仍然為100 n=1 t=Thread(target=work) t.start() t.join() print("主",n) #查看結果為0,因為同一進程內的線程之間共享進程內的數據
多線程實現socket
server端:
from threading import Thread import socket s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.bind(("127.0.0.1",8080)) s.listen(5) def action(conn,addr): while True: data=conn.recv(1024) print("來自客戶端:{addr}消息為:{data}".format(addr=addr,data=data.decode("utf-8"))) conn.send(data) if __name__ == "__main__": while True: conn,addr=s.accept() p=Thread(target=action,args=(conn,addr)) p.start()
client端:
import socket ip_port = ("127.0.0.1",8080) s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect(ip_port) while True: msg=input(">>: ").strip() if not msg:continue s.send(msg.encode("utf-8")) data=s.recv(1024).decode("utf-8") print("來自服務端:{ip},消息為:{data}".format(ip=ip_port,data=data))
Thread類的其他方法
Thread實例對象的方法 # isAlive(): 返回線程是否活動的。 # getName(): 返回線程名。 # setName(): 設置線程名。 threading模塊提供的一些方法: # threading.currentThread(): 返回當前的線程變量。 # threading.enumerate(): 返回一個包含正在運行的線程的list。正在運行指線程啟動后、結束前,不包括啟動前和終止后的線程。 # threading.activeCount(): 返回正在運行的線程數量,與len(threading.enumerate())有相同的結果。
守護線程
無論是進程還是線程,都遵循:守護xx會等待主xx運行完畢后被銷毀。需要強調的是:運行完畢并非終止運行
1 主進程在其代碼結束后就已經算運行完畢了(守護進程在此時就被回收),然后主進程會一直等非守護的子進程都運行完畢后回收子進程的資源(否則會產生僵尸進程),才會結束,
2 主線程在其他非守護線程運行完畢后才算運行完畢(守護線程在此時就被回收)。因為主線程的結束意味著進程的結束,進程整體的資源都將被回收,而進程必須保證非守護線程都運行完畢后才能結束。
1.守護線程例1
from threading import Thread import time def sayhi(name): time.sleep(2) print("%s say hello" %name) if __name__ == "__main__": t=Thread(target=sayhi,args=("egon",)) t.setDaemon(True) #必須在t.start()之前設置 t.start() print("主線程") print(t.is_alive()) """ 主線程 True """
2.守護線程例2
from threading import Thread import time def foo(): print(123) time.sleep(1) print("end123") def bar(): print(456) time.sleep(3) print("end456") t1=Thread(target=foo) t2=Thread(target=bar) t1.daemon=True t1.start() t2.start() print("main-------")鎖
同步鎖
1.多個線程搶占資源的情況
from threading import Thread import os,time def work(): global n temp=n time.sleep(0.1) n=temp-1 if __name__ == "__main__": n=100 l=[] for i in range(100): p=Thread(target=work) l.append(p) p.start() for p in l: p.join() print(n) #結果可能為99
2.解決方法:
import threading R=threading.Lock() R.acquire() """ 對公共數據的操作 """ R.release()
3.同步鎖的引用
from threading import Thread,Lock import os,time def work(): global n lock.acquire() temp=n print("子進程temp", temp) time.sleep(0.1) n=temp-1 print("子進程n", n) lock.release() if __name__ == "__main__": lock=Lock() n=5 l=[] for i in range(5): p=Thread(target=work) l.append(p) p.start() for p in l: p.join() print(n)
3.互斥鎖與join的區別
#不加鎖:并發執行,速度快,數據不安全 from threading import current_thread,Thread,Lock import os,time def task(): global n print("%s is running" %current_thread().getName()) temp=n time.sleep(0.5) n=temp-1 if __name__ == "__main__": n=100 lock=Lock() threads=[] start_time=time.time() for i in range(100): t=Thread(target=task) threads.append(t) t.start() for t in threads: t.join() stop_time=time.time() print("主:%s n:%s" %(stop_time-start_time,n)) """ Thread-1 is running Thread-2 is running ...... Thread-100 is running 主:0.5216062068939209 n:99 """ #不加鎖:未加鎖部分并發執行,加鎖部分串行執行,速度慢,數據安全 from threading import current_thread,Thread,Lock import os,time def task(): #未加鎖的代碼并發運行 time.sleep(3) print("%s start to run" %current_thread().getName()) global n #加鎖的代碼串行運行 lock.acquire() temp=n time.sleep(0.5) n=temp-1 lock.release() if __name__ == "__main__": n=100 lock=Lock() threads=[] start_time=time.time() for i in range(100): t=Thread(target=task) threads.append(t) t.start() for t in threads: t.join() stop_time=time.time() print("主:%s n:%s" %(stop_time-start_time,n)) """ Thread-1 is running Thread-2 is running ...... Thread-100 is running 主:53.294203758239746 n:0 """ #有的同學可能有疑問:既然加鎖會讓運行變成串行,那么我在start之后立即使用join,就不用加鎖了啊,也是串行的效果啊 #沒錯:在start之后立刻使用jion,肯定會將100個任務的執行變成串行,毫無疑問,最終n的結果也肯定是0,是安全的,但問題是 #start后立即join:任務內的所有代碼都是串行執行的,而加鎖,只是加鎖的部分即修改共享數據的部分是串行的 #單從保證數據安全方面,二者都可以實現,但很明顯是加鎖的效率更高. from threading import current_thread,Thread,Lock import os,time def task(): time.sleep(3) print("%s start to run" %current_thread().getName()) global n temp=n time.sleep(0.5) n=temp-1 if __name__ == "__main__": n=100 lock=Lock() start_time=time.time() for i in range(100): t=Thread(target=task) t.start() t.join() stop_time=time.time() print("主:%s n:%s" %(stop_time-start_time,n)) """ Thread-1 start to run Thread-2 start to run ...... Thread-100 start to run 主:350.6937336921692 n:0 #耗時是多么的恐怖
4.死鎖與遞歸鎖
進程也有死鎖與遞歸鎖
所謂死鎖: 是指兩個或兩個以上的進程或線程在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。此時稱系統處于死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程,如下就是死鎖
import time from threading import Lock,Thread noodle = 100 fork = 100 noodle_lock = Lock() fork_lock = Lock() def eat1(name): global noodle,fork noodle_lock.acquire() print("%s拿到面了" % name) fork_lock.acquire() print("%s拿到叉子了" % name) noodle -= 1 print("%s吃面"%name) time.sleep(0.1) fork_lock.release() print("%s放下叉子了" % name) noodle_lock.release() print("%s放下面" % name) def eat2(name): global noodle,fork fork_lock.acquire() print("%s拿到叉子了"%name) noodle_lock.acquire() print("%s拿到面了"%name) noodle -= 1 print("%s吃面"%name) time.sleep(0.1) noodle_lock.release() print("%s放下面"%name) fork_lock.release() print("%s放下叉子了"%name) for i in ["alex","wusir","egon","快老師"]: Thread(target=eat1,args=(i,)).start() Thread(target=eat2,args=(i+"2",)).start()
解決方法,遞歸鎖,在Python中為了支持在同一線程中多次請求同一資源,python提供了可重入鎖RLock。
這個RLock內部維護著一個Lock和一個counter變量,counter記錄了acquire的次數,從而使得資源可以被多次require。直到一個線程所有的acquire都被release,其他的線程才能獲得資源。上面的例子如果使用RLock代替Lock,則不會發生死鎖:
import time from threading import Thread,RLock noodle = 100 fork = 100 noodle_lock = fork_lock = RLock() def eat1(name): global noodle,fork noodle_lock.acquire() print("%s拿到面了" % name) fork_lock.acquire() print("%s拿到叉子了" % name) noodle -= 1 print("%s吃面"%name) time.sleep(0.1) fork_lock.release() print("%s放下叉子了" % name) noodle_lock.release() print("%s放下面" % name) def eat2(name): global noodle,fork fork_lock.acquire() print("%s拿到叉子了"%name) noodle_lock.acquire() print("%s拿到面了"%name) noodle -= 1 print("%s吃面"%name) time.sleep(0.1) noodle_lock.release() print("%s放下面"%name) fork_lock.release() print("%s放下叉子了"%name) for i in ["alex","wusir","egon","快老師"]: Thread(target=eat1,args=(i,)).start() Thread(target=eat2,args=(i+"2",)).start()
使用互斥鎖解決死鎖問題:
import time from threading import Thread,Lock noodle = 100 fork = 100 noodle_fork_lock = Lock() def eat1(name): global noodle,fork noodle_fork_lock.acquire() print("%s拿到面了" % name) print("%s拿到叉子了" % name) noodle -= 1 print("%s吃面"%name) time.sleep(0.1) print("%s放下叉子了" % name) noodle_fork_lock.release() print("%s放下面" % name) def eat2(name): global noodle,fork noodle_fork_lock.acquire() print("%s拿到叉子了"%name) print("%s拿到面了"%name) noodle -= 1 print("%s吃面"%name) time.sleep(0.1) print("%s放下面"%name) noodle_fork_lock.release() print("%s放下叉子了"%name) for i in ["alex","wusir","egon","快老師"]: Thread(target=eat1,args=(i,)).start() Thread(target=eat2,args=(i+"2",)).start()信號量
同進程的一樣
Semaphore管理一個內置的計數器,
每當調用acquire()時內置計數器-1;
調用release() 時內置計數器+1;
計數器不能小于0;當計數器為0時,acquire()將阻塞線程直到其他線程調用release()。
實例:(同時只有5個線程可以獲得semaphore,即可以限制最大連接數為5):
from threading import Thread,Semaphore import threading import time def func(): sm.acquire() print("%s get sm" %threading.current_thread().getName()) time.sleep(3) sm.release() if __name__ == "__main__": sm=Semaphore(5) for i in range(23): t=Thread(target=func) t.start()定時器
from threading import Timer def hello(): print("hello, world") t = Timer(1, hello) t.start() # after 1 seconds, "hello, world" will be printed線程隊列
queue隊列 :使用import queue,用法與進程Queue一樣
queue is especially useful in threaded programming when information must be exchanged safely between multiple threads.
1.先進先出
import queue # q = queue.Queue() 先進先出 # 在線程之間數據安全,自帶線程鎖的數據容器 lq = queue.LifoQueue() # 棧 先進后出 算法和數據結構中 lq.put(1) lq.put(2) lq.put(3) print(lq.get()) print(lq.get()) print(lq.get()) # print(lq.get())#如果隊列里邊沒有值了,進行get操作,會堵塞
優先級隊列
import queue pq = queue.PriorityQueue() # 優先級隊列 pq.put(3) pq.put(5) pq.put(2) print(pq.get()) print(pq.get()) print(pq.get()) pq.put("c") pq.put("a") pq.put("A") print(pq.get()) print(pq.get()) print(pq.get()) pq.put((10,"asfghfgk")) pq.put((20,"2iyfhejcn")) pq.put((15,"qwuriyhf")) print(pq.get()) print(pq.get()) print(pq.get())Python標準模塊--concurrent.futures
https://docs.python.org/dev/l...
#1 介紹 concurrent.futures模塊提供了高度封裝的異步調用接口 ThreadPoolExecutor:線程池,提供異步調用 ProcessPoolExecutor: 進程池,提供異步調用 Both implement the same interface, which is defined by the abstract Executor class. #2 基本方法 #submit(fn, *args, **kwargs) 異步提交任務 #map(func, *iterables, timeout=None, chunksize=1) 取代for循環submit的操作 #shutdown(wait=True) 相當于進程池的pool.close()+pool.join()操作 wait=True,等待池內所有任務執行完畢回收完資源后才繼續 wait=False,立即返回,并不會等待池內的任務執行完畢 但不管wait參數為何值,整個程序都會等到所有任務執行完畢 submit和map必須在shutdown之前 #result(timeout=None) 取得結果 #add_done_callback(fn) 回調函數
import os import time import random from threading import get_ident from concurrent.futures import ThreadPoolExecutor t_pool = ThreadPoolExecutor(os.cpu_count()) def func(i): time.sleep(random.randint(1,2)) print("線程:{name},任務{i}".format(name=get_ident(),i=i)) return "*"*i def call_bak(ret): print("線程:{name},返回值長度:{i}".format(name=get_ident(),i=len(ret.result()))) print("主線程:{name}".format(name=get_ident())) for i in range(1,20): t_pool.submit(func,i).add_done_callback(call_bak)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/42170.html
摘要:進程可創建多個線程來執行同一程序的不同部分。就緒等待線程調度。運行線程正常運行阻塞暫停運行,解除阻塞后進入狀態重新等待調度。消亡線程方法執行完畢返回或者異常終止。多線程多的情況下,依次執行各線程的方法,前頭一個結束了才能執行后面一個。 淺談Python多線程 作者簡介: 姓名:黃志成(小黃)博客: 博客 線程 一.什么是線程? 操作系統原理相關的書,基本都會提到一句很經典的話: 進程...
摘要:上一篇文章進程專題完結篇多進程處理的一般建議下一篇文章線程專題多線程使用的必要性進程線程進程能夠完成多任務,比如在一個電腦上可以運行多個軟件。由于占用資源少,也使得多線程程序并發比較高。 上一篇文章:Python進程專題完結篇:多進程處理的一般建議下一篇文章:Python線程專題1:多線程使用的必要性 進程VS線程 進程:能夠完成多任務,比如在一個電腦上可以運行多個軟件。線程:也能夠...
摘要:其次,解釋器的主循環,一個名為的函數,讀取字節碼并逐個執行其中的指令。所有線程都運行相同的代碼,并以相同的方式定期從它們獲取鎖定。無論如何,其他線程無法并行運行。 概述 如今我也是使用Python寫代碼好多年了,但是我卻很少關心GIL的內部機制,導致在寫Python多線程程序的時候。今天我們就來看看CPython的源代碼,探索一下GIL的源碼,了解為什么Python里要存在這個GIL,...
摘要:也提供多線程支持,而且中的線程并非是模擬出來的多線程,而是系統級別的標準庫提供了兩個模塊和。同一個變量,線程則會互相共享。例如多個線程對銀行中的某一個賬戶進行操作。但是實際情況是隨意切換線程。說到的多線程編程,就會繞不過。 該文章參考了http://www.liaoxuefeng.com/wi... 廖雪峰的教程。 一個進程至少有一個線程。Python也提供多線程支持,而且Python...
摘要:如下面的例子,在學習線程時,將文件名命名為腳本完全正常沒問題,結果報下面的錯誤。最大的問題就是的多線程程序并不能利用多核的優勢比如一個使用了多個線程的計算密集型程序只會在一個單上面運行。 本文記錄學習Python遇到的問題和一些常用用法,注本開發環境的Python版本為2.7。 一、python文件命名 在python文件命名時,一定要注意不能和系統默認的模塊名沖突,否則會報錯。如下面...
閱讀 1780·2021-11-25 09:43
閱讀 15411·2021-09-22 15:11
閱讀 2630·2019-08-30 13:19
閱讀 2015·2019-08-30 12:54
閱讀 1820·2019-08-29 13:06
閱讀 927·2019-08-26 14:07
閱讀 1619·2019-08-26 10:47
閱讀 3036·2019-08-26 10:41