進程相關知識點

一 進程對象其餘方法

pid號:當前進程的進程號python

一臺計算機上面運行着不少進程,那麼計算機是如何區分並管理這些進程服務端呢?數據庫

計算機會給每個運行的進程分配一個 PID號編程

如何查看json

​ window 電腦:windows

​ 進入 cmd 輸入 tasklist 便可查看安全

​ tasklist |findstr PID號 查看具體的進程網絡

​ mac 電腦:併發

​ 進入終端以後輸入 ps auxdom

​ ps aux | grep PID號 查看具體的進程函數

"""
一臺計算機上面運行着不少進程,那麼計算機是如何區分並管理這些進程服務端的呢?
計算機會給每個運行的進程分配一個PID號 
如何查看
	windows電腦 
		進入cmd輸入tasklist便可查看
		tasklist |findstr PID查看具體的進程
	mac電腦 
		進入終端以後輸入ps aux
		ps aux|grep PID號 查看具體的進程 
"""
from multiprocessing import Process, current_process
current_process().pid  # 查看當前進程的進程號

import os
os.getpid()  # 查看當前進程進程號
os.getppid()  # 查看當前進程的父進程進程號


p.terminate()  # 殺死當前進程
# 是告訴操做系統幫你去殺死當前進程 可是須要必定的時間 而代碼的運行速度極快
time.sleep(0.1)
print(p.is_alive())  # 判斷當前進程是否存活

​ 案例:

import time
import os

def task():
    print('子進程start')
    main_process_pid = os.getppid()
    print('當前進程的父進程號:', main_process_pid)
    time.sleep(3)
    print('子進程 over')


if __name__ == '__main__':
    p = Process(target=task)
    p.start()

    # 是告訴操做系統幫你去殺死當前進程 可是須要必定的時間 而代碼的運行速度極快
    time.sleep(4)
    p.terminate()  # 殺死進程

    #判斷當前進程是否存活
    print(p.is_alive()) #False
    print('主')

二 殭屍進程與孤兒進程(瞭解)

​ 當你開設了子進程以後 該進程死後不會馬上釋放佔用的進程號。是由於須要讓父進程可以查看到它開設的子進程的一些基本信息 佔用的pid號 運行時間。這是操做系統的機制。

一、殭屍進程

​ 殭屍進程是當子進程比父進程先結束,而父進程又沒有回收子進程,釋放子進程佔用的資源,此時子進程將成爲一個殭屍進程。

​ 也就是,只要建立子進程,就會存在殭屍進程(無論父進程是否及時回收pid 號),此時的進程 pid 號仍是被佔用的,當過多的殭屍進程存在時,就會佔用大量的 pid 號,而 pid 號是有限的,因此須要及時獲取子進程的狀態信息。

​ 殭屍進程是有害的

產生殭屍進程的程序:

#coding:utf-8
from multiprocessing import Process
import time,os

def run():
    print('子',os.getpid())

if __name__ == '__main__':
    p=Process(target=run)
    p.start()
    
    print('主',os.getpid())
    time.sleep(100)

等待父進程正常結束後會調用wait/waitpid去回收殭屍進程

若是父進程永遠不會結束,那麼該殭屍進程就會一直存在,殭屍進程過多,就是有害的

解決方法一:殺死父進程
解決方法二:對開啓的子進程應該記得使用join,join會回收殭屍進程

# coding:utf-8
from multiprocessing import Process
import time, os


def task():
    print('%s is running' % os.getpid())
    time.sleep(3)


if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    p.join()  # 等待進程p結束後,join函數內部會發送系統調用wait,去告訴操做系統回收掉進程p的id號

    #子進程被回收後,主進程查看子進程的進程號是能夠看到的
    print(p.pid)  #67389
    print('主')

'''
子進程被回收後,主進程查看子進程的進程號是能夠看到的
緣由是:
p.join()是像操做系統發送請求,告知操做系統p的id號不須要再佔用了,回收就能夠了,
子進程的PID號被操做系統回收後,此 PID 就沒有意義了,可是主進程是子進程的父親,
仍是能夠看到子進程的pid的號的
'''

解決方法三:參考博客:http://blog.csdn.net/u010571844/article/details/50419798

二、孤兒進程

​ 當父進程意外結束時(非正常結束),子進程PID不會被回收,這時候,子進程就變成了孤兒進程,操做系統至關於'孤兒院',會調用 init()來專門管理孤兒進程回收相關的資源。孤兒進程是無害的,能夠將子進程變爲守護進程,當主進程結束後,子進程跟着結束。

三 守護進程

​ 主進程將建立的子進程變成守護進程。

​ 守護進程:主進程結束,子進程跟着結束。

from multiprocessing import Process
import time


def task(name):
    print('%s子進程正在活着'% name)
    time.sleep(3)
    print('%s子進程正在死亡' % name)


if __name__ == '__main__':
    p = Process(target=task,args=('qq',))
    # p = Process(target=task,kwargs={'name':'qq'})
    p.daemon = True  # 將進程p設置成守護進程  這一句必定要放在start方法上面纔有效不然會直接報錯
    p.start()
    print('主程序結束了')

四 互斥鎖

​ 多個進程操做同一份數據的時候,會出現數據錯亂的問題

​ 針對上述問題,解決方式就是加鎖處理:

​ 將併發編程串行,犧牲效率可是保證了數據的安全

買票案例:

# coding:utf-8
from multiprocessing import Process
import json
import random
import time

#查票
def search(name):
    #文件操做讀取票數
    with open('data.json', 'r',encoding='utf-8')as f:
        dic = json.load(f)
    return dic
    # 字典取值不要用[]的形式 推薦使用get  你寫的代碼打死都不能報錯!!!

#買票
def buy(name):
    #1 用戶先查看票再買票
    dic = search(name)
    print('用戶%s查看餘票:%s'%(name, dic.get('ticket_num')))

    #模擬網絡延遲
    time.sleep(random.randint(1, 3))
    #買票,即修改後臺數據
    if dic['ticket_num'] > 0:
        #能夠買票
        dic['ticket_num'] -= 1
        with open('data.json', 'w', encoding='utf-8')as f:
            json.dump(dic, f)
        print(f'用戶{name}買票成功')
    else:
        print(f'無票,用戶{name}購買失敗')

#整合上面兩個函數
def run(name):
    search(name)
    buy(name)

if __name__ == '__main__':
    for index in range(1, 5):
        p = Process(target=run, args=(index,))
        p.start()


# 結果展現
'''
用戶1查看餘票:1
用戶2查看餘票:1
用戶3查看餘票:1
用戶4查看餘票:1
用戶4買票成功
用戶1買票成功
用戶3買票成功
用戶2買票成功
'''

能夠看到只有一張票,可是全部用戶都買到票了,有問題。緣由是:當全部進程操做數據時,會形成數據錯誤。是由於進程之間存在競爭關係,相互之間數據不共享,致使全部進程同一時間均可以操做數據

進階版:加互斥鎖處理,加鎖的意義是:同一時間只會有一個進程運行。

from multiprocessing import Process, Lock
import json
import time
import random


# 查票
def search(i):
    # 文件操做讀取票數
    with open('data.json','r',encoding='utf8') as f:
        dic = json.load(f)
    print('用戶%s查詢餘票:%s'%(i, dic.get('ticket_num')))
    # 字典取值不要用[]的形式 推薦使用get  你寫的代碼打死都不能報錯!!!


# 買票  1.先查 2.再買
def buy(i):
    # 先查票
    with open('data.json','r',encoding='utf8') as f:
        dic = json.load(f)
    # 模擬網絡延遲
    time.sleep(random.randint(1,3))
    # 判斷當前是否有票
    if dic.get('ticket_num') > 0:
        # 修改數據庫 買票
        dic['ticket_num'] -= 1
        # 寫入數據庫
        with open('data.json','w',encoding='utf8') as f:
            json.dump(dic,f)
        print('用戶%s買票成功'%i)
    else:
        print('用戶%s買票失敗'%i)


# 整合上面兩個函數
def run(i, mutex):
    search(i)
    # 給買票環節加鎖處理
    # 搶鎖
    mutex.acquire()

    buy(i)
    # 釋放鎖
    mutex.release()


if __name__ == '__main__':
    # 在主進程中生成一把鎖 讓全部的子進程搶 誰先搶到誰先買票
    mutex = Lock()
    for i in range(1,6):
        p = Process(target=run, args=(i, mutex))
        p.start()

"""
#結果展現
'''
用戶1查詢餘票:1
用戶2查詢餘票:1
用戶3查詢餘票:1
用戶4查詢餘票:1
用戶5查詢餘票:1
用戶1買票成功
用戶2買票失敗
用戶3買票失敗
用戶4買票失敗
用戶5買票失敗
'''

擴展 行鎖 表鎖

注意:
1.鎖不要輕易的使用,容易形成死鎖現象(咱們寫代碼通常不會用到,都是內部封裝好的)
2.鎖只在處理數據的部分加來保證數據安全(只在爭搶數據的環節加鎖處理便可)

​ 加鎖處理的目的,就是解決進程之間競爭關係,混亂的操做後臺數據。

​ 互斥鎖:

​ 優勢:

​ 解決了進程之間競爭關係,同一時間只能有一個進程運行,保證了數據安全

​ 缺點:

​ 將並行變成了串行,犧牲了速度,效率低

因此須要找一種仍是併發(保證運行效率),同時也能夠保證安全正確的修改後臺數據,即多個進程同時共享內存數據,這即是 IPC 通訊機制:隊列和管道。

五 進程之間通訊

一、隊列

​ 隊列和管道都是將數據存放於內存中的,經過隊列和管道能夠實現進程之間的數據傳遞。

​ 隊列:是基於管道+鎖實現的

1 Queue([maxsize]):建立共享的進程隊列,Queue是多進程安全的隊列,可使用Queue實現多進程之間的數據傳遞。
參數:maxsize是隊列中容許最大項數,省略則無大小限制。

​ 隊列 Queue模塊使用

# coding:utf-8
from multiprocessing import Queue

#建立一個隊列
q = Queue(5) #括號內能夠傳數字,表示生成的隊列對打能夠同時存放的數據量

#往隊列中存數據
q.put(111)
q.put(222)
q.put(333)
q.put(444)
q.put(555)
# q.put(666) #阻塞
# q.put(666) #緣由是,隊列可存數5 個,當隊列中數據滿了時,再往裏存時會阻塞,不會報錯

print(q.full()) #True  #判斷當前隊列是否滿了
print(q.empty()) #False #判斷當前隊列是否爲空了

'''
存取數據,存時爲了更好的取
'''

#去隊列中取數據
v1 = q.get()
v2 = q.get()
v3 = q.get()
v4 = q.get()
v5 = q.get()
print(q.empty()) #True
# v6 = q.get() #阻塞,隊列中沒有數據能夠取了,因此它會原地等待隊列中數據,不會報錯
# v6 = q.get_nowait() #報錯,隊列中沒有數據時,不會等待,直接報錯queue.Empty
# v6 = q.get(timeout=3) #沒有數據以後,原地等待獲取,三秒以後隊列中尚未數據,再報錯queue.Empty

#拋出異常方式
try:
    v6 = q.get(timeout=3)
    print(v6)

except Exception as e:
    print('一滴都沒有了')



"""
q.full()
q.empty()
q.get_nowait()
在多進程的狀況下是不精確
"""

二、IPC 機制

​ IPC 通訊機制:進程之間使用隊列或管道傳遞數據

image-20200423215231251

from multiprocessing import Queue, Process

"""
研究思路
    1.主進程跟子進程藉助於隊列通訊
    2.子進程跟子進程藉助於隊列通訊
"""
def producer(q):
    q.put('我是23號技師 很高興爲您服務')


def consumer(q):
    print(q.get())


if __name__ == '__main__':
    q = Queue()
    p1 = Process(target=producer,args=(q,))
    p2 = Process(target=consumer,args=(q,))
    p1.start()
    p2.start()

三、生產者與消費者模型

在併發編程中使用生產者和消費者模式可以解決絕大多數併發問題。該模式經過平衡生產線程和消費線程的工做能力來提升程序的總體處理數據的速度。

#生產者消費者模型總結:
    #程序中有兩類角色
        一類負責生產數據(生產者)
        一類負責處理數據(消費者)
        
    #引入生產者消費者模型爲了解決的問題是:
        平衡生產者與消費者之間的工做能力,從而提升程序總體處理數據的速度
        
    #如何實現:
        生產者<-->隊列<——>消費者
    #生產者消費者模型實現類程序的解耦和

​ 生產這與消費者模式是一種IPC 機制案例:

"""
生產者:生產/製造東西的
消費者:消費/處理東西的
該模型除了上述兩個以外還須要一個媒介
	生活中的例子作包子的將包子作好後放在蒸籠(媒介)裏面,買包子的取蒸籠裏面拿
	廚師作菜作完以後用盤子裝着給你消費者端過去
	生產者和消費者之間不是直接作交互的,而是藉助於媒介作交互
	
生產者(作包子的) + 消息隊列(蒸籠) + 消費者(吃包子的)
"""

簡單版:

# coding:utf-8
from multiprocessing import Process, Queue
import time
import random

def producer(name, food, q):

    for i in range(1, 4):
        data = f'{name}生產了{food}{i}'
        #模擬延遲
        time.sleep(random.randint(1,3))
        print(data)
        q.put(data)


def consumer(name, q):

    #消費者吃完全部的食物
    while True:
        food = q.get() #沒有數據時就會卡主
        #模擬延遲
        time.sleep(random.randint(1, 3))
        print(f'{name}吃了{food}')
        

if __name__ == '__main__':
    q = Queue()
    p1 = Process(target=producer, args=('egon', '包子', q))
    p2 = Process(target=producer, args=('tank', '雞腿', q))
    c1 = Process(target=consumer, args=('jason', q))
    p1.start()
    p2.start()
    c1.start()
    
# 結果展現
'''
egon生產了包子1
tank生產了雞腿1
jason吃了egon生產了包子1
tank生產了雞腿2
egon生產了包子2
tank生產了雞腿3
jason吃了tank生產了雞腿1
egon生產了包子3
jason吃了egon生產了包子2
jason吃了tank生產了雞腿2
jason吃了tank生產了雞腿3
jason吃了egon生產了包子3

此時進程會阻塞住
'''

此時進程已經阻塞住了,緣由是消費者運用循環在隊列中取數據,當隊列中沒有數據時,就是卡住,致使是主進程永遠不會結束。

​ 解決問題思路:

​ 能夠判斷隊列中的數據是否爲空,若是爲空,即退出循環,可是因爲在多進程下,q.full( ) 、q.empty( )、q.get_nowait( ) 不精確,因此不能使用。

​ 讓生產者在生產完畢後,往隊列中再發一個結束信號,這樣消費者在接收到結束信號後就能夠break出死循環。

進階版:

當隊列中數據被取完時,消費者進程就沒有必要存在了,能夠將消費者進程變爲守護進程,當主進程結束後(即隊列中沒有數據時),消費者進程跟着結束。

​ ps:結束信號None,不必定要由生產者發,主進程裏一樣也能夠發,但主進程須要等生產者結束後才應該發送該信號

from multiprocessing import Process, Queue
import time
import random


def producer(name,food,q):
    for i in range(5):
        data = '%s生產了%s%s'%(name,food,i)
        # 模擬延遲
        time.sleep(random.randint(1,3))
        print(data)
        # 將數據放入 隊列中
        q.put(data)


def consumer(name,q):
    # 消費者胃口很大 光盤行動
    while True:
        food = q.get()  # 沒有數據就會卡住
        # 判斷當前是否有結束的標識
        # if food is None:break
        time.sleep(random.randint(1,3))
        print('%s吃了%s'%(name,food))

if __name__ == '__main__':
    q = Queue()
    p1 = Process(target=producer,args=('大廚egon','包子',q))
    p2 = Process(target=producer,args=('馬叉蟲tank','泔水',q))
    c1 = Process(target=consumer,args=('春哥',q))
    c2 = Process(target=consumer,args=('新哥',q))
    p1.start()
    p2.start()
    # 將消費者設置成守護進程
    c1.daemon = True
    c2.daemon = True
    c1.start()
    c2.start()
    p1.join()
    p2.join()
    # 等待生產者生產完畢以後 往隊列中添加特定的結束符號
    q.put(None)  # 確定在全部生產者生產的數據的末尾
    q.put(None)  # 確定在全部生產者生產的數據的末尾

解決這種問題思路只是是發送結束信號而已,有另一種隊列提供了這種機制,就是JoinableQueue([maxsize]):這就像是一個Queue對象,但隊列容許項目(數據)的使用者通知生成者,項目已經被成功處理。通知進程是使用共享的信號和條件變量來實現的。

​ 使用 JoinableQueue 模塊生成隊列對象,內部自帶計數器。每當存入一個數時,計數器+1 ,取走一個數時,計數器 -1,方便用於多進程下,判斷隊列中是否有數據。即:

JoinableQueue的實例q的方法:
    q.task_done():使用者使用此方法發出信號,表示q.get()的返回項目已經被處理。若是調用此方法的次數大於從隊列中刪除項目的數量,將引起ValueError異常
    q.join():生產者調用此方法進行阻塞,直到隊列中全部的項目均被處理。阻塞將持續到隊列中的每一個項目均調用q.task_done()方法爲止

​ 終極版:

from multiprocessing import Process, Queue, JoinableQueue
import time
import random


def producer(name,food,q):
    for i in range(5):
        data = '%s生產了%s%s'%(name,food,i)
        # 模擬延遲
        time.sleep(random.randint(1,3))
        print(data)
        # 將數據放入 隊列中
        q.put(data)


def consumer(name,q):
    # 消費者胃口很大 光盤行動
    while True:
        food = q.get()  # 沒有數據就會卡住
        # 判斷當前是否有結束的標識
        # if food is None:break
        time.sleep(random.randint(1,3))
        print('%s吃了%s'%(name,food))
        q.task_done()  # 告訴隊列你已經從裏面取出了一個數據而且處理完畢了


if __name__ == '__main__':
    # q = Queue()
    q = JoinableQueue()
    p1 = Process(target=producer,args=('大廚egon','包子',q))
    p2 = Process(target=producer,args=('馬叉蟲tank','泔水',q))
    c1 = Process(target=consumer,args=('春哥',q))
    c2 = Process(target=consumer,args=('新哥',q))
    p1.start()
    p2.start()
    # 將消費者設置成守護進程
    c1.daemon = True
    c2.daemon = True
    c1.start()
    c2.start()
    p1.join()
    p2.join()
    # 等待生產者生產完畢以後 往隊列中添加特定的結束符號
    # q.put(None)  # 確定在全部生產者生產的數據的末尾
    # q.put(None)  # 確定在全部生產者生產的數據的末尾
    q.join()  # 等待隊列中全部的數據被取完再執行往下執行代碼
    """
    JoinableQueue 每當你往該隊列中存入數據的時候 內部會有一個計數器+1
    沒當你調用task_done的時候 計數器-1
    q.join() 當計數器爲0的時候 才日後運行
    """
    # 只要q.join執行完畢 說明消費者已經處理完數據了  消費者就沒有存在的必要了
相關文章
相關標籤/搜索