這裏雜亂的介紹window系統下 多進程相關庫multiprocess 進程 線程 併發 並行概念 初步有個大致的印象 IDE:Jetbrain pycharm 2019
python基礎系列 正在持續更新中:)
multiprocess定義 + 更改快捷鍵 + 查詢字段定義
首先我們得找到其定義(definition),選中一個詞然後在View-Quick Definition就能夠實現
建議可以更改快捷鍵爲Ctrl+D,如下圖:File-Settings:
接下來 Ctrl+D 查詢multiprocess 似乎因爲版本關係,原來直接在multiprocess能看到的class process定義沒了,而是再找Baseprocess:
class BaseProcess:
name: str
daemon: bool
authkey: bytes
def __init__(
self,
group: None = ...,
target: Optional[Callable[..., Any]] = ...,
name: Optional[str] = ...,
args: Tuple[Any, ...] = ...,
kwargs: Mapping[str, Any] = ...,
*,
daemon: Optional[bool] = ...,
) -> None: ...
def run(self) -> None: ...
def start(self) -> None: ...
def terminate(self) -> None: ...
可見process有幾個屬性:
名稱 | 說明 | 中文 |
---|---|---|
target | Callable | 可被調用的目標 |
name | str | 名字 字符串形式 |
args | Tuple | 元祖形式的參數 |
kwargs | Mapping str | 鍵值對形式的參數 |
還有幾個方法 run(運行) start(開啓) terminate(終止)
下面我們先理解一下args kwargs
args kwargs
*args = arguments 參數(形參)的意思
**kwargs = key word arguments 所以 是“鍵值參數”?
No, 鍵值對參數,還記得字典dict的鍵值對嘛,沒錯,這個kwargs只接受鍵值對傳入,其他的事不關己高高掛起,同樣args也不會越俎代庖,也只接受 多個 普通的單值參數比如字符串,數字,etc
來試試下面的例子:
def ryan_test(arg,*args,**kwargs):
print arg
print args
print kwargs
Ryan_test(1,2,3,d='4',e=5)
一個蘿蔔一個坑,形參arg就對應第一個傳入參數1,
而鍵值對(keywords) d=‘4’,e=5 被**kwargs承包了,
意味着只有*args能夠收拾剩下的爛攤子——數字 2,3,
注意是args 帶s
意味着複數,意思多個單值參數
輸出結果:
1
(2, 3)
{'e': 5, 'd': '4'}
我們再來看個案例:python的dict類構造函數是怎麼工作的:
myDICT = dict(a=1,b=2,c=3)
這個能夠生成值爲{a=1,b=2,c=3}的一個字典
其實就可以用**kwargs實現:
def ryan_dict(**kwargs):
return kwargs
print ryan_dict(a=1,b=2,c=3) == {'a':1, 'b':2, 'c':3}
你可以封裝成一個類,我比較懶就不管了:)
multiprocess使用
我們利用multiprocess的Process對象 創建 進程
創建進程的類
Process([group [, target [, name [, args [, kwargs]]]]]),由該類實例化得到的對象,表示一個子進程中的任務(尚未啓動)
創建子進程需要傳入參數 比如我就
Process(target=task_1, name="任務1",args=(1,'Ryan',))
意思 執行任務 task_1函數,子進程名字爲“任務1”,傳入參數(1,‘Ryan’)。
我們直接來看一個栗子 就理解了:
#-*- utf-8 -*-
from time import sleep
import os
from multiprocessing import Process
def task_1(second, name):
while True:
sleep(second)
print("TASK " + name + "-- "+str(os.getpid()),"--",str(os.getppid()))
def task_2(second, name):
while True:
sleep(second)
print("TASK " + name + "-- "+str(os.getpid()),"--",str(os.getppid()))
if __name__ == '__main__':
print(os.getpid())
p1 = Process(target=task_1, name="任務1",args=(1,'Ryan',))
p1.start()
p2 = Process(target=task_2, name="任務2",args=(2,'sst',))
p2.start()
這裏解釋一下 task_1 task_2 就是子進程,因爲用了start()
函數,所以是作爲子進程(ChildProcess) 我們利用getpid()
函數獲得進程的識別碼ID PID = process ID
可以看到
Ryan 子進程 PID = 9796
sst 子進程PID = 2412
那1816就是父進程(parent process) 用getppid()
也就是:get parent pid,從而 獲得其pid
打開任務管理器 taskmgr 可見我們兩個子進程 一個父進程
run start terminate
start()
啓動我們Process創建的實例——一個進程,然後會默認調用run()
,run()只負責執行屬性target
中的函數,其他的不管,意味着使用它不能實現進程,以及單核併發,或者多核並行邏輯
總之 使用start()
就對了
terminate()
就是用於終止子進程的,類似的函數還有kill()
進程 線程 多任務
進程可以說是線程(Threadings, thread有織毛衣的線的意思)的爸爸 集合。進程,就像一個包含很多線程的大家庭。線程就是兄弟姐妹,共有一個進程爸爸。兄弟姐妹間聊天比較暢通無阻,所以線程間是可以直接信息交流的,甚至可以創建、撤銷新的進程,而且共享系統資源(共享一個家),自己只能擁有運行必須的最小資源(比如自己的一個小房間),所以線程佔用資源很少。當然,每家每戶都有一塊地,這就是系統os爲進程開闢的內存空間,所以每開一個進程,就要求系統分配一份空間。
家庭雖然是公用的,但是總有公共部分,譬如廁所,一次可能只能有一個人在裏面使用。線程也是如此,常常是併發執行concurrent,所以先後不重要(代碼放的位置先後),因爲是輪詢的,總會輪到你。
但是進程間就是一個個分立的家庭了,資源不共享(你家和別家,不在一個屋檐下,資源不太可能共享),另外,信息共享也不容易,所以不是直接信息傳遞,往往通過pipe等間接傳信息的
我們想實現多任務 就有兩種方式了:
多進程 計算密集型 佔用內存等系統資源
多線程 耗時操作型 適用於下載 爬蟲 或者更廣泛的:文件IO等耗時工作
具體爲什麼 請看python 基礎(三) 線程 Threading GIL 線程同步
任務調度 併發 並行
CPU的任務調度( task dispatcher) 這裏記一下dispatch這個單詞:patch 斑點,星星點點就好像是撒出去一樣,派發出去一樣,所以dispatch 派發 派遣。CPU派遣調度人,就是dispatcher
那麼假設我們只有單核的CPU,我們需要實現多任務同時執行,然而並不可能,因爲只有一個處理器CPU,於是就可以花式調度任務——快速切換,比如10us執行任務1,10us執行task2,我們肉眼看來就好像是同時進行的。
那麼 單核CPU進行這項花式操作,就是併發(concurrenty, i.e con-current-y)
如果多核呢?目前撐死也就16核,任務數量還是遠多於核的數量,但比起單核還是輕鬆一些——比如有4核,那麼4個任務(task1,2,3,4)是真正 同時進行的
核1 10us執行task1,10us執行task5
核2 10us執行task2,10us執行task6
核3 10us執行task3,10us執行task7
核4 10us執行task4,10us執行task8
這樣的稱爲 並行(Parallelism,i.e Parallel-ism)
下一站將會更加硬核 深入理解Process 當然還是採用multiprocess庫 盡請關注:
python 基礎(二)阻塞 非阻塞 同步 異步