一、什麼是進程 進程(Process)是電腦中的程式關於某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,是操作系統結構的基礎。在早期面向進程設計的電腦結構中,進程是程式的基本執行實體;在當代面向線程設計的電腦結構中,進程是線程的容器。程式是指令、數據及其組織形式的描述,進程是程 ...
一、什麼是進程
進程(Process)是電腦中的程式關於某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,是操作系統結構的基礎。在早期面向進程設計的電腦結構中,進程是程式的基本執行實體;在當代面向線程設計的電腦結構中,進程是線程的容器。程式是指令、數據及其組織形式的描述,進程是程式的實體。我們自己在python文件中寫了一些代碼,這叫做程式,運行這個python文件的時候,這叫做進程。
狹義定義:進程是正在運行的程式的實例(an instance of a computer program that is being executed)。
廣義定義:進程是一個具有一定獨立功能的程式關於某個數據集合的一次運行活動。它是操作系統動態執行的基本單元,在傳統的操作系統中,進程既是基本的分配單元,也是基本的執行單元 1,進程的概念 第一,進程是一個實體。每一個進程都有它自己的地址空間,一般情況下,包括文本區域(text region)(python的文件)、數據區域(data region)(python文件中定義的一些變數數據)和堆棧(stack region)。文本區域存儲處理器執行的代碼;數據區域存儲變數和進程執行期間使用的動態分配的記憶體;堆棧區域存儲著活動過程調用的指令和本地變數。 第二,進程是一個“執行中的程式”。程式是一個沒有生命的實體,只有處理器賦予程式生命時(操作系統執行之),它才能成為一個活動的實體,我們稱其為進程。進程是操作系統中最基本、重要的概念。是多道程式系統出現後,為了刻畫系統內部出現的動態情況,描述系統內部各道程式的活動規律引進的一個概念,所有多道程式設計操作系統都建立在進程的基礎上。2,進程的特征 動態性:進程的實質是程式在多道程式系統中的一次執行過程,進程是動態產生,動態消亡的。 併發性:任何進程都可以同其他進程一起併發執行 獨立性:進程是一個能獨立運行的基本單位,同時也是系統分配資源和調度的獨立單位; 非同步性:由於進程間的相互制約,使進程具有執行的間斷性,即進程按各自獨立的、不可預知的速度向前推進 結構特征:進程由程式、數據和進程式控制制塊三部分組成。 多個不同的進程可以包含相同的程式:一個程式在不同的數據集里就構成不同的進程,能得到不同的結果;但是執行過程中,程式不能發生改變。 註意:同一個程式執行兩次,就會在操作系統中出現兩個進程,所以我們可以同時運行一個軟體,分別做不同的事情也不會混亂
二、併發與並行,同步與非同步
併發:偽並行,就是幾個進程共用一個cpu,幾個進程之間是時間輪換的,而這個輪換時間很短,肉眼是看不到的,所以我們肉眼看到的是幾個進程同時在進行,其他中間有停止的,只是停止時間短,這就叫併發
並行:指一個進程就用一個cpu,每個進程是沒有停止的,就是一起運行的
同步:一個任務執行完後,另一個任務才能開始執行,就是有一個任務需要等待,這樣兩個任務之間的關係是同根生的,非常緊密的
非同步:兩個任務之間沒有關聯,你執行你的,我執行我的,互相不用等待
三、進程的創建方法
1,方法一
from multiprocessing import Process #引入進程模塊, import os,time def fun1(): time.sleep(2) print('jjjj') def fun2(): time.sleep(3) print('ddada') print(os.getpid()) #獲得子進程的進程id print(os.getppid()) #獲得子進程的父進程,即主進程的id if __name__=="__main__": #記住,必須用 if main start_time=time.time() #開始時間 p=Process(target=fun2) #創建一個子進程 p.start() #開始子進程 fun1() print(os.getpid()) #獲得主進程的進程id end_time=time.time() #結束時間 print(end_time-start_time) #列印總共用時
整段程式走完用時2秒多一點,但是如果我們按照以前的寫法,先執行fun1(),再執行fun2(),至少得花5秒
2,方法二
from multiprocessing import Process #同樣引入模塊 import time class Mypro(Process): #創建一個類,繼承Process def __init__(self,name): super().__init__() self.name=name def run(self): #這是必須的方法,然後要執行的程式寫在方法裡面就行 time.sleep(3) print(self.name) if __name__ == '__main__': #記住,這裡必須寫 if main p1=Mypro('huangchun') #用自己定義的類創建一個子進程 p1.start() #子進程開始 p1.join() #這是join方法,這樣寫就是join後面的主進程程式必須等子進程執行完後在執行,兩種方法都可以用 print('我是主進程,哈哈哈哈哈哈。。。。。')
四、守護進程
把一個子進程設為守護進程之後,每當主進程執行完畢後,不管你子進程有沒有執行完畢,都要跟著結束
from multiprocessing import Process import time def fun(): time.sleep(5) print('我是子進程') if __name__ == '__main__': p=Process(target=fun) p.daemon = True #這就是把子進程設為了守護進程,但是切記,這句代碼必須寫在start()前面 p.start() print('我主進程,哈哈哈哈哈哈')
沒有設置守護進程之前,代碼要執行5秒才結束,等到子進程執行完才結束,但是設置為守護進程之後,代碼瞬間執行完畢,只列印了主進程的內容,而子進程的print直接死了
五、同步鎖(互斥鎖)
對一段程式設置同步鎖之後,不管你對這段程式開了幾個進程,但只有一個進程能執行代碼,而其他的進程需要等待前面的進程結束之後才能執行這段代碼,就相當於把開的幾個進程搞成同步了,互相約束著
這是模擬搶火車票的情景,在還沒付錢確認之前,大家都看見了有一張票,但是只有一個人能搶到票
from multiprocessing import Process,Lock #引入Lockmok import time def fun1(i): #這是查看餘票的方法 with open('piao.txt',mode='r',encoding='utf-8')as f1: num=f1.read() print('%s客戶看到票還有%s張'%(i,num)) def fun(i,lo): #這是買票的方法 time.sleep(1) lo.acquire() #這是同步鎖的開始, with open('piao.txt',mode='r',encoding='utf-8')as f1: num=f1.read() if num=='1': print('%s買到票了'%i) num=int(num)-1 elif num=='0': print('%s沒票了'%i) with open('piao.txt', mode='w', encoding='utf-8')as f1: f1.write(str(num)) lo.release() 這是同步鎖的結束 if __name__ == '__main__': lo=Lock() #這是創建一把鎖 for i in range(10): #這是迴圈創建10個客戶 fun1(i) #這是客戶查看餘票 p=Process(target=fun,args=(i,lo)) #創建子進程去搶票,然後把鎖傳給進程 p.start()
同步鎖把搶票的程式鎖起來了,所以,雖然有10個客戶都在搶票,但實際上只有一人能進入搶票程式,其他的人要等待第一個進搶票程式的人執行完後才能又進一個人,反正每次就只允許一個人在使用搶票程式,其他的人繼續排隊
六、信號量
信號量就是對一段程式設定允許最多幾個人使用,相當於一個飯店,只有四個位置,最多允許四個人吃飯,後面人要等待前面的人吃完才能進入飯店吃,前面走一個,就可以進一個
from multiprocessing import Process,Semaphore #引入Semaphore模塊 import time def fun(s,i): s.acquire() print('%s客戶在吃'%i) time.sleep(1) print('%s客戶吃完了'%i) time.sleep(1) s.release() if __name__ == '__main__': s=Semaphore(4) #創建信號量對象 for i in range(10): #迴圈創建10個人去飯店吃飯 p=Process(target=fun,args=(s,i)) #創建飯店子進程,然後把信號量傳給子進程 p.start()
七、事件
在創建事件之後預設值為False,我們可以把創建的事件對象傳給子進程,然後在子進程中事件對象.set(),使得指變為Ture。然後我們在主進程中加上事件對象.wait(),在此處,只有當值為Ture時不會阻塞,值為False就會阻塞,
從而我們可以用此方法來影響主進程的執行。
from multiprocessing import Process,Event #引入Event模塊 import time def func(e): print('子進程開始執行') time.sleep(3) print('子進程結束') e.set() #設置事件值為Ture,我們還可以通過e.clear()把值改為False if __name__ == '__main__': e=Event() #創建事件對象e,此時預設值為False p=Process(target=func,args=(e,)) #創建子進程,把e傳給子進程 p.start() print('等待子進程結束後拿到值') e.wait() #當值為False會阻塞,當值為Ture是,不會阻塞,因為我們在子進程中最好把值改為Ture,所以wait()以後的程式要等到子進程執行完才能執行 print('拿到值,執行主進程') time.sleep(1) print('主進程執行完畢')
八、隊列,消費者生產者模型,joinablequeue
1,隊列
隊列就相當於一個容器,裡面可以放數據,特點是先放進去先拿出來,即先進先出,
from multiprocessing import Queue #引入Queue模塊 q=Queue(2) #創建一個隊列對象,並給他設置容器大小,即能放幾個數據 q.put(1) #put()方法是往容器里放數據 q.put(2) q.put(3) #這是往容器里放第三個數據,由於只能放兩個,所以此處會阻塞,不會報錯,相當於死迴圈 q.put_nowait(4) #put_nowait()這也是從容器里放數據的方法,但如果容器滿了,不會阻塞,會直接報錯 q.get() #get()方法是從容器里拿數據 q.get() q.get() #這是從容器里拿第三個數據,但是容器的兩個數據拿完了,沒有數據了,此時也會阻塞,不會報錯,死迴圈 q.get(False) #這也是從容器里拿數據的方法,當沒數據時不會阻塞,直接報錯 q.get_nowait() #這也是從容器里拿數據的方法,當沒數據時不會阻塞,直接報錯 q.full() #這是查看容器是否滿了,如果滿了,返回Ture,否則返回False q.empty() #這是查看容器是否為空,如果為空,返回Ture,否則返回False
2,消費者生產者模型
就是消費者和生產者之間不是直接聯繫的,而是通過中間的一個隊列來進行聯繫的,生產者把生產的東西放進進隊列里,消費者從隊列里拿東西,其實消費者和生產者之間沒有實質的聯繫
from multiprocessing import Process,Queue import time def sheng(q): #生產者 for i in range(10): time.sleep(1) q.put(i) print('包子%i生產完畢'%i) q.put('over') def xiao(q): #消費者 while 1: time.sleep(1.5) ss=q.get() if ss=='over': break print('包子%s被吃了'%ss) if __name__ == '__main__': q=Queue(10) #創建隊列對象q p=Process(target=sheng,args=(q,)) #創建生產者子進程,把q傳給他 p1=Process(target=xiao,args=(q,)) #創建消費者子進程,也把q傳給他 p.start() p1.start()
有個問題就是,生產者要把生產結束的消息放進隊列里,讓消費者過去結束消息,消費者才知道隊列里沒有數據了,這樣消費者才能停止,如果生產者不放結束消息,當生產者結束時,即不往隊列放數據,而消費者不知道生產者已經結束,
還一直往隊列拿數據,當隊列沒數據時,消費者一邊就會阻塞。解決阻塞,有幾個消費者,生產者就應該放幾個結束消息,讓每個消費者都知道,這樣每個消費者才能結束。但是生產者又不知道有幾個消費者,所以不知奧應該放幾個結束數據,
這樣就無法解決對消費者現象。此時,我們就就可以引入joinablequeue
3,joinablequeue
他其實就是一種隊列,但她又比隊列要多兩種方法,task_done()和join()方法,正是有這兩種方法就可以解決上面的問題
from multiprocessing import Process,JoinableQueue #引入joinableQueue模塊 import time def sheng(q): #生產者 for i in range(10): time.sleep(1) q.put(i) print('包子%i生產完畢'%i) q.join() #當q收到的task_done數量等於放進q的數據數量時,生產者就結束了 def xiao(q,i): #消費者 while 1: time.sleep(1.5) ss=q.get() if ss=='over': break print('%s客戶吃包子%s'%(i,ss)) q.task_done() #消費者每從q里取一個值,就向q返回一個task_done消息 if __name__ == '__main__': q=JoinableQueue() #創建joinablequeue對象q p=Process(target=sheng,args=(q,)) #創建生產者子進程,把q傳給他 p.start() for i in range(3): #迴圈創建消費者子進程,把q傳給他 p1=Process(target=xiao,args=(q,i)) p1.daemon=True #把創建的每個消費者子進程設為守護進程 p1.start() p.join() #主進程要等到生產者子進程結束後才結束
整個個過程就是:生產者生產了10個包子,3個消費者吃包子,沒吃一個包子往q里發一個task_done,當q擁有10個task_done時,意味著10個包子吃完了,此時,生產者就結束了,接著主進程也也結束了,然後守護進程跟著結束了,即所有的消費者子進程結束,解決上面所遇到的問題