python 33 多進程(一)

多進程

1. 進程建立的兩種方式 multiprocessing

# 第一種方式
from multiprocessing import Process
import time

def task(name):
    print(f'{name} is running')
    time.sleep(2)
    print(f'{name} is gone')

if __name__ == '__main__':   # 在Windows環境下,必須在main下開啓子進程
    p = Process(target=task, args=('meet',))   # args元組形式   建立一個進程對象
    p.start()
    print("===主進程")

"""
結果:
===主進程
meet is running
meet is done
"""

# p.start():開啓子進程的命令,通知操做系統在內存中開闢一個進程空間,將主進程的數據copy加載到子進程空間中,而後操做系統調用cpu去執行子進程。    開銷較大,所以會先執行主進程的代碼。
# 第二種方式
from multiprocessing import Process
import time

class MyProcess(Process):
    
    def __init__(self, name):
        super().__init__()      # 重構父類的方法
        self.name = name
    def run(self):          # 必須寫run()
        print(f'{self.name} is running')
        time.sleep(2)
        print(f"{self.name} is gone")

if __name__ == '__main__':
    p = MyProcess('meet')   # 默認執行run()
    p.start()       # 開啓子進程的命令
    print("===主進程")

2. 進程pid (process id)

進程在內存中的惟一標識。linux

終端命令: tasklist # 查看計算機當前全部進程的pid面試

終端命令: tasklist|findstr pycharm # 查看pycharm的pidapp

代碼 :異步

import os
print(os.getpid())      # 查看當前進程的pid

print(os.getppid())     # 查看父進程的pid

3. 驗證進程之間的空間隔離

from multiprocessing import Process

lst = [1, 2]
def task():
    lst.append(3)
    print(f'子進程的:{lst}')

if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    print(f'主進程的:{lst}')
"""
主進程的:[1, 2]
子進程的:[1, 2, 3]
"""
# 主進程與子進程之間存在空間隔離。

4. 進程對象join方法

join :讓主進程等待子進程結束以後,再執行主進程。(讓主進程阻塞)
from multiprocessing import Process
import time

def task(sec):
    print('is running')
    time.sleep(sec)
    print('is gone')

if __name__ == '__main__':
    start_time = time.time()

    p1 = Process(target=task, args=(1,))
    p2 = Process(target=task, args=(2,))
    p3 = Process(target=task, args=(3,))
    p1.start()
    p2.start()
    p3.start()
# 同時開啓
    p1.join()
    print(f'p1時間:{time.time()-start_time}')  # 1s多
    p2.join()
    print(f'p2時間:{time.time()-start_time}')  # 2s多
    p3.join()
    print(f'p3時間:{time.time()-start_time}')  # 3s多
from multiprocessing import Process
import time

def task(sec):
    print('is running')
    time.sleep(sec)
    print('is gone')

if __name__ == '__main__':
    start_time = time.time()

    p1 = Process(target=task, args=(3,))
    p2 = Process(target=task, args=(2,))
    p3 = Process(target=task, args=(1,))  # 改變時間
    p1.start()
    p2.start()
    p3.start()
# 同時開啓
    p1.join()
    print(f'p1時間:{time.time()-start_time}')  # 3s多  #阻塞
    p2.join()
    print(f'p2時間:{time.time()-start_time}')  # 3s多
    p3.join()
    print(f'p3時間:{time.time()-start_time}')  # 3s多
# 面試題:將下面代碼優化
from multiprocessing import Process
import time

def task(sec):
    print('is running')
    time.sleep(sec)
    print('is gone')

if __name__ == '__main__':
    start_time = time.time()

    p1 = Process(target=task, args=(1,))
    p2 = Process(target=task, args=(2,))
    p3 = Process(target=task, args=(3,))
    p1.start()
    p2.start()
    p3.start()

    p1.join()
    p2.join()
    p3.join()
    print(f'運行時間:{time.time()-start_time}')  # 3s多
    
# 上面代碼優化:
    lst = []
    for i in range(1,4):
        p = Process(target=task, args=(i,))
        lst.append(p)
        p.start()

    for i in lst:
        i.join()
    print(f'運行時間:{time.time()-start_time}')
    
# 錯誤的優化:
    for i in range(1,4)
       p = Process(target=task, args=(i,))
       p.start()
       p.join()
    print(f'運行時間:{time.time()-start_time}')

5. 進程對象其餘屬性

p.terminate()       # 結束子進程

p.is_alive()        # 判斷子進程是否活着 True/False

p = Process(target=task, args=('meet',), age=18)    # 給子進程對象添加一個age屬性。
print(p.age)    # 18

6. 殭屍進程與孤兒進程

6.1 殭屍進程

實際中,主進程只有等待子進程結束後,主進程才結束。優化

基於Unix環境(linux,macOS):操作系統

主進程時刻監測子進程的運行狀態,當子進程結束後,一段時間內,纔會將子進程回收。code

爲何不立刻回收?對象

  1. 主進程與子進程是異步關係,主進程沒法立刻會獲取子進程是何時結束;
  2. 若是子進程結束以後立刻在內存中釋放資源,主進程就沒有辦法監測子進程的狀態。

Unix針對上面的問題,提供了一種機制:進程

全部的子進程結束後,立馬釋放掉文件的操做連接、內存等的大部分數據,可是會保留一些內容:進程號、結束時間、運行狀態,等待子進程監測、回收。

殭屍進程:子進程在結束後,主進程並無調用wait或waitpid獲取子進程的狀態信息,子進程仍然保留了一些描述內容,這種進程被稱爲殭屍進程。

​ 危害:若是父進程不對殭屍進程進行回收(wait/waitpid),產生大量的殭屍進程,這樣會佔用內存,佔用進程的pid號。

6.2 孤兒進程

父進程因爲某種緣由結束,但子進程還在運行中,則稱這些子進程爲孤兒進程。

回收機制:Unix中孤兒進程會被 init 進程回收,init 變成父進程。

殭屍進程解決方法

​ 直接殺死父進程。將全部殭屍進程變成孤兒進程,由 init進程回收。

7. 守護進程

# 子進程守護着主進程,只要主進程結束,子進程也跟着結束。

from multiprocessing import Process
import time

def task(name):
    print(f'{name} is running')
    time.sleep(2)
    print(f'{name} is gone')

if __name__ == '__main__':
    p = Process(target=task, args=('meet',))   # 建立一個進程對象
    p.daemon = True     # 放在start前,不然會報錯。
    p.start()
    time.sleep(1)
    print("===主進程")
"""
meet is running
===主進程
"""
相關文章
相關標籤/搜索