天天看點

【python系統程式設計1】多程序1 程序的含義2 Process類3 程序池Pool

【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 類程序

步驟:

  1. 定義一個類對象,繼承Process類
  2. init中先調用父類的init方法,然後處理接受的參數
  3. 重寫run方法
  4. 執行個體化類對象,通過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程序執行的流程:

  1. 向作業系統索要建立程序的資源–>建立程序
  2. 執行目标函數
  3. 程序結束,作業系統回收程序垃圾

如果某個程式需要頻繁的通過程序的方式運作多種目标函數,如果使用Process,頻繁的建立和銷毀程序會浪費大量的資源,所有就有了池的概念

Pool程序池

  1. 預先建立多個程序,放置在程序池中
  2. 需要運作目标函數時,從池中拿出一個程序去執行
  3. 執行完目标函數後,程序不銷毀,而是傳回程序池

注:如果程序池中沒有空閑程序,那麼目标函數無法執行

優勢:省去了頻繁建立程序和銷毀程序的資源

3.2 Pool的使用

步驟:

  1. 執行個體化程序池
  2. 告知程序池需要執行的目标函數
  3. 關閉程序池
  4. 堵塞主程序

例如:

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的常用方法

  1. pool = Pool(processes=None)
  • 執行個體化程序池
  • processes是池中程序的數量,預設None表示數量不限
  1. pool.apply_async(func,args=(),kwds=(),callback=None,error_back=None)
  • 告知程序池要執行的目标函數
  • args,kwds分别是元組和字典傳值
  • callback:回調函數
  • error_callback:異常會掉函數
  • 注意:callback和error_callback也會接收傳遞給func的args和kwds,是以參數要對應
  1. pool.close()
  • 不允許新的任務再進入程序池
  1. 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]