Python-進程(1)

操做系統發展史

穿孔卡片

早期程序員使用穿孔卡片編程,linux

只有一個計算機機房,一次只能讀取一個卡片,程序員

因此形成CPU的利用率極低編程

聯機批處理系統

後來出現了聯機批處理系統,支持多用戶去使用一個計算機機房併發

統計批處理系統

再後來出現了脫機批處理系統,有了高速磁盤,提升了文件的讀取速度,優勢是提升了CPU的利用率異步

單道

基於單核狀況下研究分爲單道和多道,單道即多個程序在使用CPU時是串行運行函數

多道技術

當代計算機使用的技術 是多道技術性能

空間上覆用

一個CPU能夠提供多個用戶去使用優化

時間上覆用

切換 + 保存狀態操作系統

若CPU 遇到 IO 操做,會當即將當前執行程序CPU使用權斷開

優勢:CPU利用率高

若一個程序使用CPU的時間過長,會當即將當前執行程序CPU使用權斷開

缺點:程序執行效率低

並行與併發

並行:指的是看起來像同時在運行,實際上是多個程序不停 切換 + 保存狀態

併發:真正意義上的同時運行,在多核(多個CPU)狀況下,同時執行多個程序

進程

執行中的程序叫作進程(Process),是一個動態的概念

在linux中查看進程信息:top

61546 root 20 0 15028 1196 836 R 4.4 0.1 0:00.35 top

程序與進程

程序:一堆代碼文件

進程:一堆代碼文件運行的過程

進程調度

當代操做系統調度:

時間片輪轉法 + 分級反饋隊列

一、 先來先服務調度

a,b 程序,若a程序先來,先佔用CPU

缺點:程序a先使用,程序b必須等待程序a使用CPU完畢以後才能使用

二、 短做業優先調度

a,b程序,誰的用時短,優先調度誰使用CPU

缺點:

若程序a使用時間最長,有N個程序使用時間段,

必須等待全部用時短的程序結束後才能使用

三、 時間片輪轉法

CPU執行的時間1秒鐘,若加載N個程序,將時間等分紅多n個時間片供程序執行

四、 分級反饋隊列

將執行優先級分多層級別

1級:優先級最高

2級:優先級第二,以此類推

。。。

進程的三個狀態

就緒態

全部程序建立時都會進入就緒態,準備調度

運行態

調度後的進程,進入運行態

阻塞態

凡是遇到IO操做的進程,都會進入阻塞態

若IO結束,必須從新進入就緒態

同步和異步

指的是提交任務的方式

同步:如有兩個任務須要提交,在提交第一個任務時,必須等待該任務執行結束後,才能繼續提交併執行第二個任務。

同步演示:

# coding=utf-8

import time

def test():
    # 睡一秒 也是IO操做
    time.sleep(1)

    # 計算操做不屬於IO操做
    n = 0
    for i in range(1000):
        n += i
    print(n)

if __name__ == '__main__':
    test()      # 先執行完test函數,再執行下面的代碼,同步操做
    print("hello world")

異步:

如有兩個任務須要提交,在提交第一個任務時,不須要原地等待,當即能夠提交併執行第二個任務。

阻塞與非阻塞

阻塞:

阻塞態,遇到IO 必定會阻塞

非阻塞:

就緒態

運行態

建立進程

建立進程的兩種方式

'''
建立進程方式一:
'''
from multiprocessing import Process
import time

def task(name):
    print(f"{name} 子任務start...")
    time.sleep(2)
    print(f"{name} 子任務end....")

if __name__ == '__main__':
    p = Process(target=task,args=("qinyj",))
    p.start()
    print("主進程...")

代碼解釋:

p = Process(target=task,args=("qinyj",)):實例化一個對象p,Process類參數:target=函數名,args=函數參數(必須是元組 + ,)

p.start():向操做系統發送指令開啓一個子進程,至於這個子進程何時啓動,要看機器的硬件性能

打印結果:

主進程... qinyj 子任務start... qinyj 子任務end....

'''
建立進程方式二:
'''
from multiprocessing import Process
import time

class MyProcess(Process):
    def run(self):
        print(f"子任務 start...")
        time.sleep(2)
        print(f"子任務 end....")

if __name__ == '__main__':
    p = MyProcess()
    p.start()
    print("主進程...")

代碼解釋

p = MyProcess():實例化本身寫的一個類,這個類必須繼承Process

p.start():向操做系統發送指令開啓一個子進程,至於這個子進程何時啓動,要看機器的硬件性能

打印結果:

主進程... 子任務 start... 子任務 end....

那麼爲何咱們要用if name == 'main':?,

是由於Windows在執行這個文件的時候,會自動import導入這個文件,導入這個文件至關於又從新執行了一遍裏面的代碼,這樣子進程就從新又被建立了出來,子進程而後又運行到建立子進程的代碼。。。無限循環,最後報錯。那麼在Linux中執行這個文件不會自動import導入,它所建立的子類代碼僅僅是執行的函數代碼,fork了一塊新的內存空間執行這個函數,因此不會報錯

那麼咱們爲了統一使用,最好加上if name == 'main':,意思是做爲執行文件執行的時候,判斷它的名字,條件符合了再執行下面的代碼,這樣不管哪一個平臺運行代碼都不會報錯。

join的用法

# coding=utf-8
from multiprocessing import Process
import time

def task(name):
    print(f"{name} 子任務 start...")
    time.sleep(2)
    print(f"{name} 子任務 end...")

if __name__ == '__main__':
    p1 = Process(target=task,args=("qinyj",))
    p2 = Process(target=task,args=("jack",))
    p3 = Process(target=task,args=("qyj",))

    p1.start()
    p2.start()
    p3.start()

    p1.join()
    p2.join()
    p3.join()
    print("主進程...")

p.join:用來告訴操做系統,讓子進程結束後,父進程在結束

進程間數據相互隔離

# coding=utf-8

from multiprocessing import Process

x = 100
def func():
    print("函數執行")
    global x
    x = 200
    print(f"函數內部的x:{x}")

if __name__ == '__main__':
    p = Process(target=func)
    p.start()
    p.join()
    print(f"主進程的x:{x}")

    
函數執行
函數內部的x:200
主進程的x:100

進程對象屬性

# coding=utf-8

from multiprocessing import Process
from multiprocessing import current_process
import os
import time

def task(name):
    print(f"{name} 子任務start...",f"子進程進程號:{current_process().pid}")
    print(f"{name} 子任務start...",f"子進程進程號:{os.getpid()}",f"父進程進程號{os.getppid()}")
    time.sleep(200)
    print(f"{name} 子任務end.....",current_process().pid)

if __name__ == '__main__':
    p = Process(target=task,args=("qinyj",))
    p.start()
    time.sleep(1)

    print(p.is_alive())
    p.terminate()
    time.sleep(1)

    print(p.is_alive())

    p.join()
    print("主程序start..",os.getpid())
    print("主主進程start...",os.getppid())

    
    
qinyj 子任務start... 子進程進程號:9132
qinyj 子任務start... 子進程進程號:9132 父進程進程號7348
True
False
主程序start.. 7348
主主進程start... 2812

代碼解釋:

p.terminate():直接告訴操做系統,終止子進程

print(p.is_alive()):打印子進程的存活狀態(True or False)

current_process().pid:獲取當前的pid號

os.getpid():獲取當前的pid號

os.getppid():獲取當前父進程的pid號

cmd中查看進程號:tasklist |findstr 進程號

C:\Users\Administrator>tasklist | findstr 6724      # 查看子進程pid號即python解釋器的進程
python.exe                    6724 Console                    1     30,576 K

C:\Users\Administrator>tasklist | findstr 2812      # 查看主進程pid號即pycharm解釋器的進程
pycharm64.exe                 2812 Console                    1    909,528 K

由進程產生的pid號會自動 由主進程回收,若是子進程存在,主進程死掉了,那麼子進程就是殭屍進程

兩種進程號回收的方法條件:

一、 join:主進程能夠等待子進程結束 pid一塊兒被回收

二、 主進程正常結束,守護進程開啓,子進程與主進程 pid被回收

守護進程

# coding=utf-8
from multiprocessing import Process
from multiprocessing import current_process
import os
import time

def task(name):
    print(f"{name} 子進程start...",current_process().pid)
    time.sleep(5)
    print(f"{name} 子進程end...",current_process().pid)

if __name__ == '__main__':
    p = Process(target=task,args=("qinyj",))
    p.daemon = True
    p.start()
    time.sleep(1)   # 停留1s 讓子進程啓動
    print("主進程啓動...")

    
qinyj 子進程start... 5680
主進程啓動...

代碼解釋:

p.daemon = True:添加守護進程,True

守護進程表示的是 主進程死的時候,不管子進程在幹什麼,直接幹掉子進程,跟着主程序一塊兒死掉

殭屍進程

殭屍進程指的是子進程已經結束,但pid號還存在沒有被銷燬,能夠比喻人死了,身份證尚未註銷,那這個pid就會一直被佔用,那麼操做系統的進程號是有限的,若是產生大量的殭屍進程,將由於沒有可用的進程號而致使系統不能產生新的進程,此爲殭屍進程的危害,應當避免

那麼咱們能夠經過查找這個pid號kill 手動殺掉回收

殭屍進程的缺點:佔用pid號,佔用操做系統的資源

孤兒進程

孤兒進程指的是子進程還在執行,但父進程意外結束,可是有操做系統優化機制,會提供一個孤兒院,專門幫那些已經死了的主進程 回收那些沒有父親的子進程,在linux操做系統中,這個孤兒院就是init進程,pid號是1

1 root 20 0 19364 644 424 S 0.0 0.1 0:17.45 init

相關文章
相關標籤/搜索