python高級之多任務2(進程)

3.3進程

3.3.1進程概述

通俗理解一個運行起來的程序或者軟件叫做進程

(1)每次啓動一個進程都需要向操作系統索要運行資源(內存),進程是操作系統資源分配的基本單位

(2)進程只提供運行資源,真正幹活的是線程,線程是執行程序中對應的代碼的, 默認一個進程默認只提供一個線程(主線程),當然還可以在一個進程裏面開闢多個線程

(3) 如何理解進程:把公司類比成進程,公司會給我們提供辦公資源(辦公桌椅,辦公電腦等),公司裏面的員工利用公司裏面的資源去幹活,所以公司可以想成進程,員工就是線程

3.3.2進程和線程的對比

(1)進程是操作系統資源分配的基本單位,線程是cpu調度的基本單位,cpu調度哪個線程,哪個線程去執行對應的代碼

(2)進程之間不共享全局變量, 線程之間共享全局變量,但是要注意資源競爭的問題,解決問題的方式是互斥鎖或者線程同步

(3)多進程開發比單進程多線程開發代碼的穩定性要強,但是多進程開發比多線程開發資源開銷要多

(4)多進程開發某一個進程掛掉不會影響其它進程的運行,單進程開發該進程掛掉所有的線程都死掉了, 多線程開發會共享進程中資源

(5)先有進程,線程是依附在進程裏面的,默認一個進程會提供一個線程(主線程)

3.3.3創建進程

import multiprocessing

sub_process = multiprocessing.Process(group=None, target=show_info, name="myprocess", args=("楊鈺瑩", 18))

sub_process.start()

參數說明:

(1)group: 進程組, 默認值必須是None

(2)target:執行的目標函數

(3)name: 進程名稱, 如果不指定進程名字的格式: Process-1,....

(4)args: 以元組的方式傳參

(5)kwargs:以字典的方式傳參

3.3.4獲取進程、進程編號

import  multiprocessing 
import os
def work():
    # 獲取當前進程
    current_process = multiprocessing.current_process()
    print("work:", current_process)
    # 擴展-獲取進程編號
    print("work的進程編號:", current_process.pid, os.getpid())
    # 獲取父進程的編號
    print("work進程的父進程編號:", os.getppid())
    for i in range(10):
        print("工作中....")
        time.sleep(0.2)
        # 擴展: 根據進程編號殺死指定進程
        os.kill(os.getpid(), 9)

3.3.5進程注意點

(1)進程之間不共享全局變量

(2)主進程等待子進程完成再退出

若不想主進程一直等待,可用:

①守護主進程

sub_process = multiprocessing.Process(target=test)

sub_process.daemon = True

time.sleep(1)

sub_process.start()

②銷燬子進程

sub_process = multiprocessing.Process(target=test)

time.sleep(1)

sub_process.terminate()

sub_process.start()

3.3.6進程間通信——Queue

可以使用multiprocessing模塊的Queue實現多進程之間的數據傳遞,Queue本身是一個消息隊列(先進先出)程序

創建消息隊列:

queue = multiprocessing.Queue()

說明:

(1)初始化Queue()對象時(例如:queue=Queue()),若括號中沒有指

定最大可接收的消息數量,或數量爲負值,那麼就代表可接受的消息數

量沒有上限(直到內存的盡頭)

(2)放入數據:

queue.put(xxxx)

注意:消息隊列可以放入任何數據類型。如果隊列滿了使用put放入

數據會等待,直到隊列有空閒位置才能放入數據。如果隊列滿了使用

put_nowait不會等待,放入數據不成功會崩潰。 爲了安全起見放入數

據統一使用put

(3)獲取隊列裏的數據

value = queue.get()

print(value)

注意: 如果隊列空了,那麼使用get取值會等待,直到隊列有數據才

能獲取數據。如果隊列空了, 使用get_nowait不會等待,如果取值不

成功則崩潰。爲了安全起見獲取數據使用get。

(4)獲取隊列的個數,注意:mac不能使用此方法

queue_size = queue.qsize()

(5)判斷隊列是否爲空

queue.empty()——不靠譜

如果隊列爲空,返回True,反之False

注意:判斷隊列是否爲空的時候,不會等數據全部寫入到隊列以

後再判斷,有可能出現獲取的隊列是空的

解決辦法:保證數據全部寫入到隊列裏面以後再去做判斷

延時

time.sleep(0.001)

根據個數判斷

if queue.qsize() == 0:

print("隊列爲空")

else:

print("隊列不爲空")

3.3.7進程池

池子裏面放的都是進程,進程的創建是進程池根據執行任務的情況自動創建進程,進程池會合理利用現有的進程完成多任務

import multiprocessing
import time


# 拷貝任務
def copy_work():
    print("複製中....,", multiprocessing.current_process().pid)
    # 默認進程池創建的進程是守護主進程,那麼主進程退出進程池中的子進程就直接銷燬
    time.sleep(1)



if __name__ == '__main__':
    # 創建進程池, 3:表示進程池中最大進程的個數
    pool = multiprocessing.Pool(3)
    # 使用進程池執行對應任務,默認大批量的複製任務讓進程池幫我們完成
    for i in range(10):
        # 同步執行對應的任務,是一個任務執行完成另外一個任務才能執行
        # pool.apply(copy_work)
        # 異步執行,多個任務一起執行,任務之間不會等待
        pool.apply_async(copy_work)
    #注意:異步執行要有下面的操作:關閉進程池和等待
    # 關閉進程池,表示進程池以後不再接收對應的任務了,主進程會根據現有進程池的任務執行完成以後程序就可以退出了
    pool.close()
    # 主進程等待進程池把任務執行完成以後程序再退出
    pool.join()

    # 提醒: 進程池會根據任務執行情況自動創建進程,進程池會盡量少創建進程,可以利用現有的進程完成多任務

3.3.8進程池中的Queue

(1)如果要使用Pool創建進程,就需要使用multiprocessing.Manager()中的Queue()

(2)創建進程池中queue

queue = multiprocessing.Manager().Queue(3)

(3)使用同步的方式執行任務

# 參數要使用元組傳參

pool.apply(write_data, (queue, ))

(4)使用異步的方式去執行任務 ,異步執行任務會返回一個ApplyResult 對象

result = pool.apply_async(write_data, (queue,))

print(result)

(5)擴展:主進程等待異步任務執行完成以後代碼再繼續往下執行

result.w·ait()

pool.apply_async(read_data, (queue,))

# 異步執行任務,主進程需要等待進程池執行完成以後程序再退出

pool.close()

pool.join()

3.3.9案例:文件夾拷貝器

import os
import shutil
import multiprocessing
import time


# 拷貝文件的任務
def copy_work(src_dir, dst_dir, file_name):
    print(multiprocessing.current_process().pid)
    # 源文件的路徑
    src_file_path = src_dir + "/" + file_name
    # 目標文件的路徑
    dst_file_path = dst_dir + "/" + file_name
    # 打開目標文件
    with open(dst_file_path, "wb") as dst_file:
        # 打開源文件獲取源文件的數據把數據寫入到目標文件裏面
        with open(src_file_path, "rb") as src_file:
            while True:
                # 讀取數據
                src_file_data = src_file.read(1024)
                if src_file_data:
                    # 把源文件的數據寫入到目標文件裏面
                    dst_file.write(src_file_data)
                else:
                    break
    time.sleep(0.5)
if __name__ == '__main__':
    # 源目錄
    src_dir = "test"
    # 目標目錄
    dst_dir = "/home/python/Desktop/test"

    # 判斷文件夾是否存在
    if os.path.exists(dst_dir):
        # 刪除目標目錄
        shutil.rmtree(dst_dir)
    # 創建文件夾
    os.mkdir(dst_dir)
    # 獲取源目錄的文件列表
    file_name_list = os.listdir(src_dir)

    # 創建進程池
    pool = multiprocessing.Pool(3)

    # 遍歷文件列表獲取對應的文件名
    for file_name in file_name_list:
        # 使用進程池完成多任務的文件拷貝
        pool.apply_async(copy_work, (src_dir, dst_dir, file_name))
    # 關閉進程池
    pool.close()
    # 主進程等待進程池執行完成以後程序再退出
    pool.join()

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章