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()