進程 1.含義:電腦中的程式關於某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位。說白了就是一個程式的執行實例。 執行一個程式就是一個進程,比如你打開瀏覽器看到我的博客,瀏覽器本身是一個軟體程式,你此時打開的瀏覽器就是一個進程。 2.進程的特性 一個進程里可以有多個子進程 新的進程的 ...
進程
1.含義:電腦中的程式關於某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位。說白了就是一個程式的執行實例。
執行一個程式就是一個進程,比如你打開瀏覽器看到我的博客,瀏覽器本身是一個軟體程式,你此時打開的瀏覽器就是一個進程。
2.進程的特性
-
一個進程里可以有多個子進程
-
新的進程的創建是完全拷貝整個主進程
-
進程里可以包含線程
-
進程之間(包括主進程和子進程)不存在數據共用,相互通信(瀏覽器和python之間的數據不能互通的),要通信則要藉助隊列,管道之類的
3.進程和線程之間的區別
-
線程共用地址空間,而進程之間有相互獨立的空間
-
線程之間數據互通,相互操作,而進程不可以
-
新的線程比新的進程創建簡單,比開進程的開銷小很多
-
主線程可以影響子線程,而主進程不能影響子進程
4.在python中,進程與線程的用法就只是名字不同,使用的方法也是沒多大區別
5.簡單實例
1)創建一個簡單的多進程:
#!usr/bin/env python
#-*- coding:utf-8 -*-
# author:yangva
import multiprocessing,time
def func(name):
time.sleep(1)
print('hello',name,time.ctime())
ml = []
for i in range(3):
p = multiprocessing.Process(target=func,args=('yang',))
p.start()
ml.append(p)
for i in ml:
i.join() #註意這裡,進程必須加join方法,不然會導致僵屍進程
運行結果:
不管怎麼說,反正報錯了,同樣的代碼,在python自帶的IDLE里試試:
沒有任何東西就結束了。好的,這裡要說下了,按照我個人的理解,當你用pycharm或者IDLE時,pycharm或者IDLE在你的電腦里本身也是一個進程,並且預設是主進程。所以在pycharm會報錯,而在IDLE里運行就是空白,個人理解,對不對暫且不談,後期學到子進程時再說。
解決辦法就是,其他的不變,加一個if __name == '__main__'判斷就行:
這樣就解決了,好的,你現在可以體會到那句話了,進程與線程的用法就只是名字不同,使用的方法也是沒多大區別。不多說,自行體會。而運行結果看到的時間是同步的,那麼這進程才是真正意義上的並行運行。
2)自定義類式進程
#!usr/bin/env python #-*- coding:utf-8 -*- # author:yangva import multiprocessing,time class myprocess(multiprocessing.Process): def __init__(self,name): super(myprocess,self).__init__() self.name = name def run(self): time.sleep(1) print('hello',self.name,time.ctime()) if __name__ == '__main__': ml = [] for i in range(3): p = myprocess('yang') p.start() ml.append(p) for j in ml: j.join()
運行結果:
然後setDaemon之類的方法和線程也是完全一致的。
3)每一個進程都有根進程,換句話,每一個進程都有父進程
#!usr/bin/env python #-*- coding:utf-8 -*- # author:yangva import multiprocessing,time,os def info(): print('mudule name:',__name__) print('parent process:',os.getppid()) #父進程號 print('son process:',os.getpid()) #子進程號 if __name__ == '__main__': info() print('-----') p = multiprocessing.Process(target=info,args=[]) p.start() p.join()
運行結果:
而查看我本機的進程:
可以知道,6204就是pycharm,正是此時的根進程,而主進程就是我這個py文件(由__main__可知),接著再往下的子進程等等等的。
6.多進程間的通信和數據共用
首先我們都已經知道進程之間是獨立的,不可以互通,並且數據相互獨立,而在實際開發中,一定會遇到需要進程間通信的場景要求,那麼我們怎麼搞呢
有兩種方法:
- pipe
- queue
1)使用queue通信
在多線程那裡已經學過queue了,創建queue的方式,q = queue.Queue(),這種創建是創建的線程queue,並不是進程queue。創建進程queue的方式是:
#!usr/bin/env python #-*- coding:utf-8 -*- # author:yangva import multiprocessing def func(q,name,age): #這裡必須要把q對象作為參數傳入才能實現進程之間通信 q.put({'name':name,'age':age}) if __name__ == '__main__': q = multiprocessing.Queue() #創建進程queue對象 ml = [] for i in range(3): p = multiprocessing.Process(target=func,args=(q,'yang',21)) p.start() ml.append(p) print(q.get()) #獲取queue信息 print(q.get()) print(q.get()) for i in ml: i.join()
運行結果:
好的,已經通過queue實現通信,那麼細心的朋友可能會想,此時的queue到底是同一個呢還是copy的呢?開始測試,碼如下:
#!usr/bin/env python #-*- coding:utf-8 -*- # author:yangva import multiprocessing def func(q,name,age): q.put({'name':name,'age':age}) print('id:',id(q)) if __name__ == '__main__': q = multiprocessing.Queue() ml = [] print('id:',id(q)) for i in range(3): p = multiprocessing.Process(target=func,args=(q,'yang',21)) p.start() ml.append(p) print(q.get()) print(q.get()) print(q.get()) for i in ml: i.join()
在Windows平臺運行結果:
Linux的ubuntu下是這樣的:
這就不好怎麼說了,我個人的理解,線程和進程這類與電腦硬體(CPU,RAM)等有聯繫的都有不確定因素,姑且認為在Windows平臺里queue是copy的,在Linux里是同一個吧,並且據經驗人士表示,在macbook上也是同一個。
還有個問題, 假如使用的queue是線程式的呢?
代碼其他都沒變,只改了這裡:
結果:
雖然報錯了,但是卻有一個關鍵點,提示的是不能pickle線程鎖對象,也就是說剛纔我們使用的queue是進程對象,所以可以pickle,註意了,這裡就是關鍵點,使用了pickle,那麼也就是說,在Windows平臺里是copy的,如果不是copy,就不需要存在pickle對吧?直接拿來用就是啊,幹嘛要pickle之後取的時候再反pickle呢對吧?
再看Linux下呢,由於Linux預設是python2,所以模塊包名稍微有點不同
結果阻塞住了,但是前面的還是出來了,看到的id果然還是一樣的。
這裡就有三點需要註意:(個人理解,如有誤望指正)
1.進程里的確不能使用線程式queue
2.Windows平臺的進程式queue是copy的
3.Linux平臺的線程式和進程式都是同一個,但是如果在進程里使用線程式queue會阻塞住
但我個人覺得copy更有安全性
2)使用pipe通信
#!usr/bin/env python #-*- coding:utf-8 -*- # author:yangva import multiprocessing def func(conn): conn.send('約嗎?') #子進程發送數據 print(conn.recv()) #接受數據,不能加參數1024之類的 conn.close() #子進程關閉連接 if __name__ == '__main__': parent_conn,son_conn = multiprocessing.Pipe() #創建pipe對象,父進程,子進程 ml = [] p = multiprocessing.Process(target=func,args=(son_conn,)) p.start() print(parent_conn.recv()) #父進程接受數據,不能加參數1024之類的 parent_conn.send('不約') #發送數據 p.join() #join方法是進程特有
運行結果:
這樣就聯繫上了,相信你發現了,基本和前面的socket差不多,不過唯一的不同是recv()方法不能加參數,不信的話,你加來試試
反觀線程通信,相信你會覺得進程比線程更方便
當然pipe也可以有多個:
#!usr/bin/env python #-*- coding:utf-8 -*- # author:yangva import multiprocessing,time def func(conn): conn.send('約嗎?') #子進程發送數據 print(conn.recv()) conn.close() #子進程關閉連接 if __name__ == '__main__': parent_conn,son_conn = multiprocessing.Pipe() #創建pipe對象,父進程,子進程 ml = [] for i in range(3): p = multiprocessing.Process(target=func,args=(son_conn,)) p.start() ml.append(p) print(parent_conn.recv()) #父進程接受數據,不能加參數1024之類的 parent_conn.send('不約') for i in ml: i.join()
運行結果:
7.進程之間數據共用——manager
比較簡單,就利用了進程里的manager對象下的各個數據類型,其他的很簡單的,我就不註釋了
#!usr/bin/env python #-*- coding:utf-8 -*- # author:yangva import multiprocessing def func(l,d,num): l.append(num) d[num] = num if __name__ == '__main__': with multiprocessing.Manager() as manager: l = manager.list() d = manager.dict() ml = [] for i in range(6): p = multiprocessing.Process(target=func,args=(l,d,i)) p.start() ml.append(p) for i in ml: i.join() print('d:',d) print('l:',l)
運行結果:
這樣是不是就實現了數據共用了?
好的,進程也解析完了