【python系統程式設計1】多程序
- 1 程序的含義
-
- 1.1 程式、程序與線程的管理
- 1.2 多程序的運作原理
- 1.3 并發與并行
- 1.4 pid和ppid
- 2 Process類
-
- 2.1 入門案例
- 2.2 傳遞參數
- 2.3 常用方法與屬性
- 2.4 pid和ppid
- 2.5 類程序
- 2.6 Process程序通信
-
- 2.6.1 程序通信的問題
- 2.6.2 簡單的通信案例
- 2.6.3 隊列的常用方法與參數
- 2.7 Process程序的生命周期
- 3 程序池Pool
-
- 3.1 池的概念
- 3.2 Pool的使用
- 3.3 Pool的常用方法
- 3.4 Pool通信
- 3.5 Pool的生命周期
1 程序的含義
1.1 程式、程序與線程的管理
一個運作的程式,會包括至少一個程序,每一個程序中包括至少一個線程
1.2 多程序的運作原理
每個cpu在機關時間隻能運作一個程序
cpu在程序間的切換速度飛快,宏觀表現為多個程序同時運作,但微觀上仍有先後順序
1.3 并發與并行
并發:看似一起運作,宏觀上的感念
并行:真正一起運作,微觀的概念
1.4 pid和ppid
每個運作的程序,都有一個唯一的pid(類似身份證)
父程序的pid,稱為ppid(parent pid:爸爸的身份證)
2 Process類
2.1 入門案例
from multiprocessing import Process
import time # 用于延時
# 定義子線程運作的目标函數
def func():
for i in range(3):
print(i)
# main:表示主程序
if __name__ == '__main__':
# 執行個體化process程序對象,目标執行func函數
process = Process(target=func)
# 運作processs程序
process.start()
# 主程序也運作func函數
func()
2.2 傳遞參數
args:根據元組中的索引對應函數接受的參數
kwargs:根據字典中的key對應函數接受的參數
from multiprocessing import Process
# 目标方法,接受兩個參數
def func(param1,param2):
print(param1,param2)
if __name__ == '__main__':
# 根據索引對應傳入參數
process1 = Process(target=func,args=('value1','value2'))
process1.start()
# 根據字典的鍵對應傳入參數
process2 = Process(target=func,kwargs={'param1':'value1','param2':'value2'})
process2.start()
2.3 常用方法與屬性
指令 | 說明 |
---|---|
name | 設定或擷取程序的别名 |
pid | 擷取程序的pid |
start() | 運作 |
is_alive() | 判斷程序是否正在運作 |
join(timeout) | 子程序運作完,或已堵塞timeout秒後,放行主程序 |
terminate() | 停止子程序 |
例如:
from multiprocessing import Process
import time # 用于延時
# 子程序運作的目标方法
def func():
for i in range(10000):
print(i)
time.sleep(1)
if __name__ == '__main__':
process = Process(
target=func,
name='子程序1', # 1.name 為程序起别名
)
# 2.start 運作程序
process.start()
# 3.pid 得到子程序的pid
print('子程序的pid=',process.pid)
# 4.is_alive() 判斷程序是否正在運作
print(process.is_alive())
# 5.join() 堵塞主程序
process.join(timeout=2)
# 6.terminate() 停止子程序
process.terminate()
2.4 pid和ppid
指令 | 說明 |
---|---|
os.getpid() | 得到目前程序的pid |
os.getppid() | 得到目前程序的父程序的pid |
process.pid | 得到process程序的pid |
from multiprocessing import Process
import os
def func():
print('子程序的pid=',os.getpid())
print('子程序的ppid=',os.getppid())
if __name__ == '__main__':
process = Process(target=func)
process.start()
print('process的pid=',process.pid)
print('主程序的pid=',os.getppid())
2.5 類程序
步驟:
- 定義一個類對象,繼承Process類
- init中先調用父類的init方法,然後處理接受的參數
- 重寫run方法
- 執行個體化類對象,通過start運作類程序
例如:
from multiprocessing import Process
# 1.定義類對象,繼承Process
class MyProcess(Process):
def __init__(self,param):
# 2.1 調用父類的init方法
Process.__init__(self)
# 2.2 處理接受參數
self.param = param
# 3. 定義run方法(相當于程序執行的目标方法)
def run(self):
print(self.param)
if __name__ == '__main__':
# 4.1 執行個體化類對象
myProcess = MyProcess('value')
myProcess.start()
注意:
- start()是以多程序的方式,去運作類中的run()方法
- 如果外部直接調用run()方法,則不是以多程序的方式運作的
2.6 Process程序通信
2.6.1 程序通信的問題
注意:程序之間不能共享全局變量,是以Global關鍵字不能作為程序通信的橋梁
程序通信的方法:
- 執行個體化程序通信隊列
- 程序執行的目标函數,或類對象需要接受通信隊列
2.6.2 簡單的通信案例
from multiprocessing import Process,Queue # 1.導入程序通信隊列
# 2.程序目标函數需要接受隊列
def func(q):
while True:
print(q.get())
if __name__ == '__main__':
# 3.執行個體化程序通信隊列
queue = Queue()
# 4.傳遞隊列
Process(target=func,args=(queue,)).start()
# 5.隊列通信例子:
import time
for i in range(5):
queue.put(i)
time.sleep(1)
2.6.3 隊列的常用方法與參數
1.queue = Queue(maxsize=-1):
- 執行個體化程序通信隊列
- maxsize:隊列中資料的最大個數,-1表示個數不限
2.queue.get(block=True,timeout=None)
- 從隊列中取資料
- block:是否堵塞(如果隊列中沒有資料,是否等待)
- timeout:最大堵塞的時間(隻有設定block=True才能發揮作用),超過最大堵塞時間會報異常
3.queue.get_nowait()
- 以不堵塞的方式取隊列中的資料,如果隊列中沒有資料,則報異常
4.queue.put(obj,block=True,timeout=None)
- 把obj對象放入隊列
- block:是否堵塞(如果隊列中的資料達到最大個數,是否堵塞)
- timeout:堵塞的最大時間,超過則報異常
5.queue.put_nowait(obj)
- 以不堵塞的方式放入隊列資料,如果隊列資料個數達到最大值,則報異常
6.queue.qsize()
- 傳回目前隊列中資料的個數
7.queue.empty()
- 判斷隊列是否為空,傳回True|Flase
8.queue.full()
- 判斷隊列中資料是否達到最大值,傳回True|False
2.7 Process程序的生命周期
生命周期:
- 關閉主程序,子程序也會關閉
- 主程序沒有需要執行的代碼,而子程序還在執行,主程序會等待子程序執行完再關閉
3 程序池Pool
3.1 池的概念
Process程序執行的流程:
- 向作業系統索要建立程序的資源–>建立程序
- 執行目标函數
- 程序結束,作業系統回收程序垃圾
如果某個程式需要頻繁的通過程序的方式運作多種目标函數,如果使用Process,頻繁的建立和銷毀程序會浪費大量的資源,所有就有了池的概念
Pool程序池
- 預先建立多個程序,放置在程序池中
- 需要運作目标函數時,從池中拿出一個程序去執行
- 執行完目标函數後,程序不銷毀,而是傳回程序池
注:如果程序池中沒有空閑程序,那麼目标函數無法執行
優勢:省去了頻繁建立程序和銷毀程序的資源
3.2 Pool的使用
步驟:
- 執行個體化程序池
- 告知程序池需要執行的目标函數
- 關閉程序池
- 堵塞主程序
例如:
from multiprocessing import Pool
import time
def func(param):
for i in range(3):
print(param)
time.sleep(1)
if __name__ == '__main__':
# 1.執行個體化程序池(池中有兩個程序)
pool = Pool(2)
# 2.告訴程序池要執行的目标函數
for i in range(10):
pool.apply_async(
func=func, # 指定目标函數
args=(i,) # 傳遞參數
)
# 3.關閉程序池
pool.close()
# 4.堵塞主程序
pool.join()
3.3 Pool的常用方法
- pool = Pool(processes=None)
- 執行個體化程序池
- processes是池中程序的數量,預設None表示數量不限
- pool.apply_async(func,args=(),kwds=(),callback=None,error_back=None)
- 告知程序池要執行的目标函數
- args,kwds分别是元組和字典傳值
- callback:回調函數
- error_callback:異常會掉函數
- 注意:callback和error_callback也會接收傳遞給func的args和kwds,是以參數要對應
- pool.close()
- 不允許新的任務再進入程序池
- pool.join()
- 堵塞主程序
- 注意:pool.join()必須在pool.close()後執行
3.4 Pool通信
類似Process程序通信,唯一不同的地方是導包:
from multiprocessing import Manager
queue = Manager().Queue()
Pool通信隊列Queue的常用方法與上面Process中列舉的相同
3.5 Pool的生命周期
生命周期:
- 關閉主程序,Pool中的程序也關閉
- 如果主程序中沒有可執行的代碼,不會等待Pool中的程序執行完,而會直接關閉
這也是為什麼需要使用pool.join()的原因
聯系方式:[email protected]