併發編程

一、進程基本操做

os.getpid獲取進程id:

import os
import time
print('start')
time.sleep(40)  #配合time.sleep()
print(os.getpid(),os.getppid(),'end')  #獲取進程id os.getpid()  os.getppid()  父進程id
------------結果:
start
5028 1960 end  # 5028 進程id 當前文件執行的進程id,程序結束,進程中止;1960 父進程pycharm id  

# pid   process id
# ppid  parent process id
# 子進程
# 父進程 在父進程中建立子進程
# 在pycharm中啓動的全部py程序都是pycharm的子進程

1557764818794

進程建立,執行本文件內的函數:

import os
import time
from multiprocessing import Process  #[1]從多進程ing導入進程P  Process類

def func():
    print('start',os.getpid())  #[3]開啓的進程函數作的操做:獲取進程id,由於這是子進程執行的程序內容
    time.sleep(1)       #因此這裏的os.getpid()表示的是這個開啓的進程的pid
    print('end',os.getpid())

if __name__ == '__main__':
    p = Process(target=func)  #[2]建立進程對象  Process(目標=函數名) #目標=xx#查看源碼可知  Process類init方法中有傳參 target
    p.start()   # 異步 調用開啓進程的方法 可是並不等待這個進程真的開啓,
    print('main :',os.getpid())
----------------結果:
main : 3012  #[4]先執行start,可是先打印main。由於是異步的,調用開啓進程的方法,p.start 執行以後就會建立進程,將傳進去取的target加括號()執行。無需等待這個子進程開啓和執行,當前程序繼續往下執行。
start 5748  #[5]這兩個獲取的id 都是子進程程序運行裏的,獲取的都是子進程pid,值是同樣的。
end 5748

>tasklist|findstr "py" #Windows查看進程的命令

1557765208621

1557816673647

進程是併發的:

import os
import time
from multiprocessing import Process

def eat():
    print('start eating',os.getpid())
    time.sleep(1)
    print('end eating',os.getpid())

def sleep():
    print('start sleeping',os.getpid())
    time.sleep(1)
    prinst('end sleeping',os.getpid())

if __name__ == '__main__':
    p1 = Process(target=eat)    # [1]建立一個即將要執行eat函數的進程對象
    p1.start()                  # [2]開啓一個進程
    p2 = Process(target=sleep)  # [3]建立一個即將要執行sleep函數的進程對象
    p2.start()                  # [4]開啓進程
    print('main :',os.getpid())
--------------------------結果:
main : 6216  #[5]異步,父進程沒有等到子進程結束再往下執行,因爲進程建立須要花費時間,全部在子進程建立的過程當中,主進程已經往下執行並打印出主進程的pid.
start eating 6468  #[6]由此處可知,代碼先建立並執行p1進程,再建立並執行p2進程,可是兩者並無說先執行完p1
start sleeping 7120 #進程,而後才執行p2進程。也就是說建立多個進程,進程運行是異步的,是併發執行。
end eating 6468
end sleeping 7120

if __name__ == '__main__':的做用:以及操做系統建立進程的方式區別

import os
import time
from multiprocessing import Process
def func():
    print('start',os.getpid())
    time.sleep(1)
    print('end',os.getpid())

if __name__ == '__main__':
    p = Process(target=func)
    p.start()   # 異步 調用開啓進程的方法 可是並不等待這個進程真的開啓
    print('main :',os.getpid())
-------------結果:
main : 6420
start 6736
end 6736



# 操做系統建立進程的方式不一樣
# windows操做系統執行開啓進程的代碼
    # 實際上新的子進程須要經過import父進程的代碼來完成數據的導入工做
    # 因此有一些內容咱們只但願在父進程中完成,就寫在if __name__ == '__main__':下面
# ios linux操做系統建立進程 fork

錯誤一:
若是 在windows裏面沒有使用if __name__ == '__main__':,那麼會報錯:
 if __name__ == '__main__':
                freeze_support()
 The "freeze_support()" line can be omitted if the program
        is not going to be frozen to produce an executable.
  • if __name__ == '__main__':的做用:css

  • 兩個文件打印__name__,分別執行文件以後打印__main__的做用:linux

    1)執行文件中打印本文件中的__name__,結果顯示__main__;執行文件中import導入其它文件,其它文件中有個打印__name__ 即print(__name__),在本文件中顯示那個文件的文件名,即模塊名字。ios

    2)所以,當本程序執行中存在if __name__ == '__main__':,因爲等式成立,因此if判斷下的程序會執行。而導入的其它文件中存在if __name__ == '__main__':,因爲在本文件導入時,__name__ 不等於 '__main__':,而是等於那個的模塊名,全部其它文件中的if __name__ == '__main__':下的內容不會執行數據庫

    3)由此可知:若是想讓本文件中的程序在其它文件導入時執行,就不要寫在if __name__ == '__main__':下;反之,若是想讓本文件中的程序在其它文件導入時不執行,就寫在if __name__ == '__main__':下;編程

    4)在以上基礎上:在Windows中,在文件中開啓一個子進程以導入的方式在子進程內存空間中執行一遍。若是開啓進程的命令沒有寫在if __name__ == '__main__':下,那麼開啓一個子進程後,子進程再開啓一個子進程,這樣就會出現遞歸建立子進程,全部會報錯;json

    若是是在Linux或者mac操做系統下,是fork建立進程,不須要寫if __name__ == '__main__':也不會報錯。建立進程的方式不一樣,他們是將父進程內存中的數據拷貝一份子進程中。windows

  • print([__name__])
    if __name__ == '__main__':
        # 控制當這個py文件被看成腳本直接執行的時候,就執行這裏面的代碼
        # 當這個py文件被看成模塊導入的時候,就不執行這裏面的代碼
        print('hello hello')
    # __name__ == '__main__'
        # 執行的文件就是__name__所在的文件
    # __name__ == '文件名'
        # __name__所在的文件被導入執行的時候

主進程和子進程之間的關係

# 主進程和子進程之間的關係
import os
import time
from multiprocessing import Process
def func():
    print('start',os.getpid())
    time.sleep(10)
    print('end',os.getpid())

if __name__ == '__main__':
    p = Process(target=func)
    p.start()   # 異步 調用開啓進程的方法 可是並不等待這個進程真的開啓
    print('main :',os.getpid())
-------------------結果:
main : 6636
start 6752
end 6752

    # 主進程沒結束 :等待子進程結束
    # 主進程負責回收子進程的資源
    # 若是子進程執行結束,父進程沒有回收資源,那麼這個子進程會變成一個殭屍進程

    # 主進程的結束邏輯
        # 主進程的代碼結束
        # 全部的子進程結束
        # 給子進程回收資源
        # 主進程結束


import os
import time
from multiprocessing import Process
def func():
    print('start',os.getpid())
    time.sleep(1)
    print('end',os.getpid())
print("mcw")
if __name__ == '__main__':
    p = Process(target=func)
    p.start()   # 異步 調用開啓進程的方法 可是並不等待這個進程真的開啓
print('main :',os.getpid())
----------------結果:
mcw
main : 5244
mcw
main : 3392
start 3392
end 3392
#1)建立一個進程對象,並開啓進程。對子進程的建立開啓都是在父進程程序中執行的。子進程首先將父進程全部的執行程序導入並執行一次,由於if __name__ == '__main__':在子進程中不知足條件,因此沒有執行。
2)在父進程的程序中先打印"mcw",建立好進程後,再打印「main」此處打印父進程pid。子進程導入這個程序的內容,先打印"mcw",再打印「main」此處打印子進程pid,因程序是在子進程中運行。而後將父進程中建立子進程時傳進去的函數執行一下。打印start ,end。建立對象,對象.start()至關於給函數名加個括號,讓函數執行。由於子進程的內存空間中已經有func函數了,因此進程對象.start(),在這個空間中找到func函數並執行。
3)疑問?建立的子進程是先導入內容,再執行傳進來的這個函數嗎?

主進程怎麼知道子進程結束了的呢?

#[1] 主進程怎麼知道子進程結束了的呢?
    # 基於網絡、文件
# [2]join方法 :阻塞,直到子進程結束就結束
import time
from multiprocessing import Process
def send_mail():
    time.sleep(3)
    print('發送了一封郵件')
if __name__ == '__main__':
    p = Process(target=send_mail)
    p.start()   # 異步 非阻塞
    # time.sleep(5)
    print('join start')
    p.join()    #[3] 同步 阻塞 直到p對應的進程結束以後才結束阻塞
    print('5000封郵件已發送完畢')
-----------------結果:  
join start
發送了一封郵件
5000封郵件已發送完畢 
#[4]若是沒有對象.join,因爲沒有阻塞,父進程和子進程是異步執行,並且建立進程花時間長些。在程序打印'join start'以後,會先打印'5000封郵件已發送完畢',因爲程序中遇到對象.join(),因此會阻塞,等待子進程結束,父進程才繼續執行這個程序。
# 開啓5個進程,給公司的5000我的發郵件,發送完郵件以後,打印一個消息「5000封郵件已發送完畢」
import time
import random
from multiprocessing import Process
def send_mail(a):   
    time.sleep(random.random())
    print('發送了一封郵件',a)

if __name__ == '__main__':
    l = []
    for i in range(5):
        p = Process(target=send_mail,args=(i,))
        p.start()
        l.append(p)
    print(l)
    for p in l:p.join()  #[1]join起到阻塞,將以前的異步非阻塞變成同步阻塞 
    # 阻塞 直到上面的五個進程都結束
    print('5000封郵件已發送完畢')
-----------------------結果:
[<Process(Process-1, started)>, <Process(Process-2, started)>, <Process(Process-3, started)>, <Process(Process-4, started)>, <Process(Process-5, started)>]
發送了一封郵件 3
發送了一封郵件 4
發送了一封郵件 0
發送了一封郵件 1
發送了一封郵件 2
5000封郵件已發送完畢

分解分析上面if __name__ == '__main__':後面的 代碼:安全

需求應爲:同時發送多個郵件,直到全部郵件發送完畢才發送'5000封郵件已發送完畢'
(1)狀況一:
建立五個進程,沒有join阻塞。還沒發郵件就已經打印發送完畢,有問題。
if __name__ == '__main__':
    for i in range(5):
        p = Process(target=send_mail,args=(i,))
        p.start()
    print('5000封郵件已發送完畢')
 ------------結果:
 5000封郵件已發送完畢
發送了一封郵件 0
發送了一封郵件 1
發送了一封郵件 3
發送了一封郵件 2
發送了一封郵件 4
(2)狀況二:
由於每一個都join阻塞,發送五次郵件,每次都是上一封郵件發送完畢才能發送下一封郵件,串行 效率低,不是想要的需求。
if __name__ == '__main__':
    for i in range(5):
        p = Process(target=send_mail,args=(i,))
        p.start()
        p.join()
    print('5000封郵件已發送完畢')
-------------結果:
發送了一封郵件 0
發送了一封郵件 1
發送了一封郵件 2
發送了一封郵件 3
發送了一封郵件 4
5000封郵件已發送完畢
(3)狀況三:
併發執行五個進程,可是隻阻塞最後一個了。由於for循環五次以後p表明最後一個,p.join()只阻塞最後一個。因此,打印5000封郵件已發送完畢  每次都是最後建立的那個進程運行完畢纔打印,可是其它進程若是有比較慢才執行完畢的,那麼就是尚未發送完全部郵件,就已經打印發送全部郵件完畢。這樣有問題。
if __name__ == '__main__':
    for i in range(5):
        p = Process(target=send_mail,args=(i,))
        p.start()
    p.join()
    print('5000封郵件已發送完畢')
-------------結果:
發送了一封郵件 2
發送了一封郵件 1
發送了一封郵件 4
5000封郵件已發送完畢
發送了一封郵件 3
發送了一封郵件 0
(4)狀況四:
[1]將每次建立並開啓進程的進程對象追加到列表,循環列表對每個進程執行 對象.join()阻塞。
[2]對已經結束的p.join就至關於pass,沒影響。
[3]沒有結束的就會阻塞住,主程序會等待沒有結束的子進程結束。子進程是併發執行,而且每一個都有阻塞
[4]完成的阻塞一下沒問題,沒完成的阻塞一下,全部阻塞結束,程序往下執行,即完成全部子進程結束,父進程從新從阻塞後面開始執行。全部子進程結束時間總長是以最長子進程時間爲全部子進程結束時間的
假設運行時間是: 2 4 1 3 2
 全部子進程結束所需時間4s,時間花費是最大時長的那個子s進程所花費的時長。
if __name__ == '__main__':
    l = []
    for i in range(5):
        p = Process(target=send_mail,args=(i,))
        p.start()
        l.append(p)
    for p in l:p.join()  #join起到阻塞,將以前的異步非阻塞變成同步阻塞
    # 阻塞 直到上面的五個進程都結束
    print('5000封郵件已發送完畢')

#綜上:想要實現併發而且還要等到全部的子進程結束以後,父進程才結束。那麼就將每一個進程追加到列表,循環列表對每一個列表元素進行阻塞。

本程序中的建立子進程對象,也能夠是傳進導入的其它模塊中的函數

xx.py----------------------
import os,time
def func():
    print('start',os.getpid())
    time.sleep(1)
    print('end',os.getpid())
test.py------------------
import os
import xx
from multiprocessing import Process
if __name__ == '__main__':
    p = Process(target=xx.func) #此處能夠是導入的函數,也能夠用模塊.函數執行,估計類中方法也是能夠的。
    p.start()   # 
    print('main :',os.getpid())
-----------------------test.py打印結果:
main : 5768
start 5896
end 5896

往子進程中傳參(思考,傳參字典等特殊傳參呢?):

from multiprocessing import Process
def func(arg1,arg2):
    print(arg1,arg2)
if __name__ == '__main__':
    p = Process(target=func,args=(1,2))
    p.start()
-------------結果:
1 2
#給進程要執行的函數傳參,在建立進程對象的時候添加args=(1,)。假如是單個參數,那麼加個逗號纔是元組。元組內元素與形參位置一一對應
from multiprocessing import Process
def func(arg1):
    print(arg1)
if __name__ == '__main__':
    p = Process(target=func,args=(1,))
    p.start()
------------結果:
1

傳參總結:
[1]給進程要執行的函數傳參,在建立進程對象的時候添加args=(1,)。假如是單個參數,那麼加個逗號纔是元組。元組內元素與形參位置一一對應
# 總結
# 1.開啓一個進程
    # 函數名(參數1,參數2)
    # from multiprocessing import Process
    # p = Process(target=函數名,args=(參數1,參數2))
    # p.start()
    #注意:函數名能夠是從其它模塊導入過來的。
# 2.父進程  和 子進程
# 3.父進程會等待着全部的子進程結束以後才結束
    # 爲了回收資源
# 4.進程開啓的過程當中windows和 linux/ios之間的區別
    # 開啓進程的過程須要放在if __name__ == '__main__'下
        # windows中 至關於在子進程中把主進程文件又從頭至尾執行了一遍
            # 除了放在if __name__ == '__main__'下的代碼
        # linux中 不執行代碼,直接執行調用的func函數
# 5.join方法
    # 把一個進程的結束事件封裝成一個join方法
    # 執行join方法的效果就是 阻塞直到這個子進程執行結束就結束阻塞

    # 在多個子進程中使用join
    # p_l= []
    # for i in range(10):
        # p = Process(target=函數名,args=(參數1,參數2))
        # p.start()
        # p_l.append(p)
    # for p in p_l:p.join()
    # 全部的子進程都結束以後要執行的代碼寫在這裏
    
    
#注意:[1]發起建立子進程,子進程不是當即執行,有一點點延遲,而且父進程和子進程是異步的,數據等資源互相隔離,執行互不干擾。
[2]父子各自打印這個func地址是不同的,表示子進程中的資源是獨立存在的。是隔離開來的。
[3]主進程程序執行完後,主進程還沒結束,在等待子進程結束;主進程要負責回收子進程的資源;若是子進程執行結束,父進程沒有回收資源,那麼這個子進程會成爲一個殭屍進程
[4]主進程結束邏輯:

守護進程:

# 有一個參數能夠把一個子進程設置爲一個守護進程
import time
from multiprocessing import Process
def son1(a,b):
    while True:
        print('shou hu')
        time.sleep(0.5)
def son2():
    for i in range(5):
        print('qi ta jin cheng')
        time.sleep(1)
if __name__ == '__main__':
    p = Process(target=son1,args=(1,2)) 
    p.daemon = True ##必定要在p.start()前設置,設置p爲守護進程,禁止p建立子進程,而且父進程代碼執行結束,p即終止運行
    p.start()      # 把p子進程設置成了一個守護進程
    p2 = Process(target=son2)
    p2.start()
    print("================")
    time.sleep(2)
    print("-----------------------") ##只要終端打印出這一行內容,那麼守護進程p也就跟着結束掉了
-----------------結果:
================
qi ta jin cheng
shou hu
shou hu
qi ta jin cheng
shou hu
shou hu
-----------------------
qi ta jin cheng
qi ta jin cheng
qi ta jin cheng

#守護進程建立:[1]對象.daemon=True變守護進程
            [2]必定要在p.start()前設置,設置p爲守護進程,禁止p建立子進程,而且父進程代碼執行結束,p即終止運行
# 守護進程是隨着主進程的代碼結束而結束的
    # 生產者消費者模型的時候
    # 和守護線程作對比的時候
# 全部的子進程都必須在主進程結束以前結束,由主進程來負責回收資源

關於守護進程須要強調兩點:
其一:守護進程會在主進程代碼執行結束後就終止
其二:守護進程內沒法再開啓子進程,不然拋出異常:AssertionError: daemonic processes are not allowed to have children
若是咱們有兩個任務須要併發執行,那麼開一個主進程和一個子進程分別去執行就ok了,若是子進程的任務在主進程任務結束後就沒有存在的必要了,那麼該子進程應該在開啓前就被設置成守護進程。主進程代碼運行結束,守護進程隨即終止;

使用場景:隨着主進程的代碼結束而結束就用守護進程

進程其它方法is_alive() terminate():

import time
from multiprocessing import Process

def son1():
    while True:
        print('is alive')
        time.sleep(0.5)

if __name__ == '__main__':
    p = Process(target=son1)
    p.start()      # 異步 非阻塞
    print(p.is_alive())
    time.sleep(1)
    p.terminate()   # 異步的 非阻塞
    print(p.is_alive())   # 進程還活着 由於操做系統還沒來得及關閉進程
    time.sleep(0.01)
    print(p.is_alive())   # 操做系統已經響應了咱們要關閉進程的需求,再去檢測的時候,獲得的結果是進程已經結束了
--------------結果:
True
is alive
is alive
True
False

# 什麼是異步非阻塞?
    # terminate
[1]查看進程是否運行狀態  對象.is_alive
[2]終止進程操做 對象.終止(terminate())   終止以後判斷是否還活着,結果是還活着一段時間,即終止也是須要時間終止的
終止進程操做 對象.終止   終止以後判斷是否還活着,結果是還活着一段時間。異步,不等他  非阻塞
start異步非阻塞

面向對象建立進程

import os
import time
from multiprocessing import Process

class MyProcecss2(Process):
    def run(self):
        while True:
            print('is alive')
            time.sleep(0.5)

class MyProcecss1(Process):
    def __init__(self,x,y):
        self.x = x
        self.y = y
        super().__init__()
    def run(self):
        print(self.x,self.y,os.getpid())
        for i in range(5):
            print('in son2')
            time.sleep(1)

if __name__ == '__main__':
    mp = MyProcecss1(1,2)
    mp.daemon = True
    mp.start()
    print(mp.is_alive())
    mp.terminate()
    mp2 = MyProcecss2()
    mp2.start()
    print('main :',os.getpid())
    time.sleep(1)
----------------結果:
True
main : 5808
is alive
is alive

面向對象編程簡寫:服務器

class MyProcecss1(Process):  #
    def run(self):
        for i in range(5):
            print('jincheng1')
            time.sleep(1)
if __name__ == '__main__':
    mp = MyProcecss1()  
    mp.start()
----------------結果:
jincheng1
jincheng1
jincheng1
jincheng1
jincheng1
class MyProcecss1(Process): #1)建立一個類,繼承Process。
    def __init__(self,x,y): #4)建立init初始化方法,往裏面傳參,而後run裏面就可使用傳進去的參數了。怎麼傳進去?建立實例的時候傳入參數。
        self.x = x
        self.y = y
        super().__init__()#5)若是子進程不須要傳參,init方法能夠不寫。由於父類中init中有不少建立進程的步驟自定義沒有,沒法建立進程,因此super().沒法建立時報錯以下結果:
    def run(self): #2)必須寫一個run方法,run方法裏面寫子進程的運行代碼
        for i in range(5):
            print('jincheng%s',self.x,self.y)
            time.sleep(1)
if __name__ == '__main__':
    mp = MyProcecss1(1,2)   # #3)建立這個類的對象,往對象中傳參實例變量。不能往run方法裏面傳參,由於沒有調用run,是start開啓進程類中封裝了的運行的run。那麼能夠將參數放到init
    mp.start()
-----------------結果:
# assert self._popen is None, 'cannot start a process twice'
#AttributeError: 'MyProcecss1' object has no attribute '_popen'
jincheng%s 1 2
jincheng%s 1 2
jincheng%s 1 2
jincheng%s 1 2
jincheng%s 1 2

1557833734424

面向對象建立進程。
1)建立一個類,繼承Process。
2)必須寫一個run方法,run方法裏面寫子進程的運行代碼
3)建立這個類的對象,往對象中傳參實例變量。不能忘run方法裏面傳參,由於沒有調用run,是start開啓進程類中封裝了的運行的run。那麼能夠放到init
4)建立init初始化方法,往裏面傳參,而後run裏面就可使用傳進去的參數了。怎麼傳進去?建立實例的時候傳入參數。
5)若是子進程不須要傳參,init方法能夠不寫。由於父類中init中有不少建立進程的步驟自定義沒有,沒法建立進程,因此super()

同步阻塞 好比p.join 等待子進程結束父進程才往下執行網絡

# Process類總結:
# 開啓進程的方式
    # 面向函數
        # def 函數名:要在子進程中執行的代碼
        # p = Process(target= 函數名,args=(參數1,))
    # 面向對象
        # class 類名(Process):
            # def __init__(self,參數1,參數2):   # 若是子進程不須要參數能夠不寫
                # self.a = 參數1
                # self.b = 參數2
                # super().__init__()
            # def run(self):
                # 要在子進程中執行的代碼
        # p = 類名(參數1,參數2)
    # Process提供的操做進程的方法
        # p.start() 開啓進程      異步非阻塞
        # p.terminate() 結束進程  異步非阻塞

        # p.join()     同步阻塞
        # p.isalive()  獲取當前進程的狀態

        # daemon = True 設置爲守護進程,守護進程永遠在主進程的代碼結束以後自動結束

進程直接通訊:

併發編程 買票系統 鎖的概念:

併發進程作什麼事。
併發編程結合網絡編程

一:併發編程結合網絡編程
1)併發編程建立子進程。子進程有本身的獨立空間
2)網絡編程tcp編程,多個客戶端鏈接服務端,一個客戶端在服務端收發信息的部分就建立一個進程,這樣每一個客戶端對應服務端一個進程,實現多個客戶端鏈接服務端。

二:車票購票系統:
購票多我的買票,一個一個排隊購買,多人同時購買(建立子進程,實現併發)
用戶太多,一臺服務器處理併發子進程數量有限,再來一臺服務器響應處理。多臺服務器同時響應請求就要出現調度的服務器,負載均衡服務器。
買票信息存在數據庫。沒臺處理服務器一個數據庫,信息不一致。公用一臺數據庫

處理服務器,處理一條買票信息,數據庫就要減小一個票,兩者通訊,網絡通訊。通訊有延遲。
處理服務器和數據庫的通訊:
10我的搶一張票

  • 車票購票系統分析:

    1)查票一個函數

    2)10我的搶就是10個進程來執行這個函數,誰查詢的解決,誰要傳參進去,傳參 args,傳的是元組

    3)買票了:買票一個函數 time模擬網絡延遲

    4)這時,1張票結果多我的都買到了,這時是有問題的,

    5)問題分析,由於多我的是有併發的,一我的讀取到一張票,網絡延遲,寫入減小一張票須要0.1秒,在0.1秒內多我的又來併發讀到一張票,作一樣的操做。因而多我的在0.1秒內成功執行了操做.一張票多人都讀取到買到了

    6)多我的來數據庫讀取請求。有個鎖,只有一我的成功執行以後才能下一我的進來操做。只有一個進程對同一個文件操做完以後別的進程才能進來操做這個文件,保證數據一致性

    7)lock.acquire()加鎖,沒有搶到鎖的進程被阻塞了,等待釋放鎖。代碼前加鎖,一個進程執行完釋放鎖,suo.釋放()

    8)查票不加鎖,買票必須加鎖,不加鎖數據會出錯,買票是串行,非並行。
    因此:
    一、若是在一個併發的場景下涉及到某部份內容時須要修改一些全部進程共享的數據資源 須要加鎖來維護數據的安全
    二、在數據安全的基礎上,才考慮效率問題。
    這裏查票函數併發,買票函數涉及到修改共享數據因此須要加鎖串行,保證數據一致性。併發是多進程實現,一個網絡鏈接(網絡編程)建立一個子進程。
    三、同步存在的意義:

    9)

    10)進程與進程間數據隔離,任何對共享數據資源修改都存在數據安全問題,加鎖解決

    11)加鎖還能夠上下文管理,用with封裝了異常處理的,不會出現release不能解鎖的問題 release是放在finamly的
    12)with加鎖能夠放在買票函數裏,也能夠放在task任務裏。在task任務裏,加鎖而後執行買票函數

    13)在子進程中 對須要加鎖的代碼 進行with lock:

    14)查票函數 買票函數 任務函數(查看不需加鎖 併發,加鎖買票 串行 目的 保證數據一致性,不會混亂(若是修改共享數據也併發,每一個人都讀到一張票,而後作買票,再修改共享數據減去一張票,就是多我的買到票了))

# 併發 可以作的事兒
# 1.實現可以響應多個client端的server
# 2.搶票系統

#'ticket_count.txt'文本內容:
{"count": 0}

import time
import json
from multiprocessing import Process,Lock

def search_ticket(user):
    with open('ticket_count.txt') as f:
        dic = json.load(f)
        print('%s查詢結果  : %s張餘票'%(user,dic['count']))

def buy_ticket(user,lock):
    # with lock:
    # lock.acquire()   # 給這段代碼加上一把鎖
        time.sleep(0.02)
        with open('ticket_count.txt') as f:
            dic = json.load(f)
        if dic['count'] > 0:
            print('%s買到票了'%(user))
            dic['count'] -= 1
        else:
            print('%s沒買到票' % (user))
        time.sleep(0.02)
        with open('ticket_count.txt','w') as f:
            json.dump(dic,f)
    # lock.release()   # 給這段代碼解鎖

def task(user, lock):
    search_ticket(user)
    with lock:
        buy_ticket(user, lock)

if __name__ == '__main__':
    lock = Lock()
    for i in range(10):
        p = Process(target=task,args=('user%s'%i,lock))
        p.start()
 -------------結果:
 user0查詢結果  : 3張餘票
user0買到票了
user1查詢結果  : 3張餘票
user1買到票了
user3查詢結果  : 1張餘票
user3買到票了
user6查詢結果  : 0張餘票

--------------------------
# 1.若是在一個併發的場景下,涉及到某部份內容
    # 是須要修改一些全部進程共享數據資源
    # 須要加鎖來維護數據的安全
# 2.在數據安全的基礎上,才考慮效率問題
# 3.同步存在的意義
    # 數據的安全性

# 在主進程中實例化 lock = Lock()
# 把這把鎖傳遞給子進程
# 在子進程中 對須要加鎖的代碼 進行 with lock:
    # with lock至關於lock.acquire()和lock.release()
# 在進程中須要加鎖的場景
    # 共享的數據資源(文件、數據庫)
    # 對資源進行修改、刪除操做
# 加鎖以後可以保證數據的安全性 可是也下降了程序的執行效率


加鎖的場景:
加鎖的優缺點:
加鎖方法:
加鎖的緣由:
什麼是鎖:
多把鎖:

鎖的簡單理解:

場景:修改共享數據資源
方法:
從多進程模塊導入Lock類
實例化類
with lock:
    函數執行(傳參lock進入)
    def 函數(lock)不須要操做對lock
    
lock = Lock()
with lock:
    須要加鎖解鎖的函數執行,將鎖對象傳進去

進程之間的數據隔離*

from multiprocessing import Process
n = 100
def func():
    global n
    n -= 1

if __name__ == '__main__':
    p_l = []
    for i in range(10):
        p = Process(target=func)
        p.start()
        p_l.append(p)
    for p in p_l:p.join()
    print(n)
-------------結果:
100

隊列的使用:

  • 兩個隔離的進程間進行通訊:
    一個父進程中多個子進程,一個子進程一個任務。子進程完成任務進程間須要通訊。假設是計算求和,就須要返回計算結果。
    進程間通訊方式: 隊列 文件 管道 (數據表彷佛也是文件)

  • 隊列:

    1)導入模塊

    2)建立隊列,

    3)隊列傳到函數

    4)函數內調用,將數據put

    5)外 get

  • 隊列原理:

1)至關於第三個文件,在內存中的

2)子進程put 父進程get

3)先進先出

  • 隊列:基於
    文件家族的sockct實現,不是基於網絡的。存取數據基於piclkle,lock
    兩個進程往同一個隊列裏面存取數據,有可能兩個進程往同一個地方存取到數據,全部要保證數據安全,隊列加鎖了,串行

  • 隊列 sockct piclkle,lock

    管道 sockct piclkle

    隊列基於管道+鎖實現

    管道基於sockct piclkle實現

    (重寫隊列方法,會報錯缺乏東西,說明內部使用了)

  • 管道 :一個發一個收 效率高,多個的話那麼數據不安全,它自己沒有鎖,全部效率高

    隊列:多個收發

  • q.get在隊列爲空時發生阻塞。這樣就會等待有值再往下執行
    建立隊列傳一個參數 爲隊列大小 。 q = Queue(5)
    put多個,滿了阻塞
    put_nowait 當隊列爲滿的時候再向隊列中放數據 會報錯而且會丟失數據
    get_nowait # 在隊列爲空的時候 直接報錯

    get 阻塞 get_nowait非阻塞

  • 三個方法不許確,有隱患
    緣由:在併發中獲取三個值的路途中就已經被別的進程修改了,
    好比:獲取的到是空的,值在返回的途中,有個進程已經放入值,這時空的結果就是有問題的。

    q.empty

    q.qsize

    q.full

    初始化裏面初始化方法

from multiprocessing import Queue,Process
# 先進先出
def func(exp,q):
    ret = eval(exp)
    q.put({ret,2,3})
    q.put(ret*2)
    q.put(ret*4)

if __name__ == '__main__':
    q = Queue()
    Process(target=func,args=('1+2+3',q)).start()
    print(q.get())
    print(q.get())
    print(q.get())
-----------------結果:
{2, 3, 6}
12
24
# Queue基於 天生就是數據安全的
    # 文件家族的socket pickle lock
# pipe 管道(不安全的) = 文件家族的socket pickle
# 隊列 = 管道 + 鎖
# from multiprocessing import Pipe
# pip = Pipe()
# pip.send()
# pip.recv()
import queue
from multiprocessing import Queue
q = Queue(5)
q.put(1)
q.put(2)
q.put(3)
q.put(4)
q.put(5)   # 當隊列爲滿的時候再向隊列中放數據 隊列會阻塞
print('5555555')
try:
    q.put_nowait(6)  # 當隊列爲滿的時候再向隊列中放數據 會報錯而且會丟失數據
except queue.Full:
    pass
print('6666666')
--------------結果:
5555555
6666666import queue
from multiprocessing import Queue
q = Queue(5)
q.put(1)
q.put(2)
q.put(3)
q.put(4)
q.put(5)   # 當隊列爲滿的時候再向隊列中放數據 隊列會阻塞
print('5555555')
try:
    q.put_nowait(6)  # 當隊列爲滿的時候再向隊列中放數據 會報錯而且會丟失數據
except queue.Full:
    pass
print('6666666')

print(q.get())
print(q.get())
print(q.get())   # 在隊列爲空的時候會發生阻塞
print(q.get())   # 在隊列爲空的時候會發生阻塞
print(q.get())   # 在隊列爲空的時候會發生阻塞
try:
    print(q.get_nowait())   # 在隊列爲空的時候 直接報錯
except queue.Empty:pass
-----------------結果:
5555555
6666666
1
2
3
4
5
from multiprocessing import Queue 
q = Queue(5) #從多進程導入Queue 隊列, Queue(n)隊列建立一個進程隊列對象。最大存放n個數據
q.put("mcw") #put將數據放入進程隊列
q.put("xiao")
aa=q.get() #get 將數據拿出進程隊列 mcw 先進先出
bb=q.get() #xiao
print(aa)
print(bb)
----------------結果:
mcw
xiao
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息