1. 程式人生 > >python_11(網絡編程)

python_11(網絡編程)

超時 文件描述 exc bar -- 精確 主播 網紅 第6章

第1章 ucp協議
1.1 特性
1.2 缺陷
1.3 UDP協議實時通信
第2章 socket的更多方法
2.1 面向鎖的套接字方法
2.1.1 blocking設置非阻塞
2.1.2 BlockingIOError
2.2 面向文件的套接字的函數
2.3 驗證客戶端鏈接的合法性
第3章 並發編程
3.1 進程特性
3.2 並發和並行
3.3 同步異步阻塞非阻塞
3.3.1 同步異步
3.3.2 阻塞與非阻塞
3.3.3 小結
3.4 python中進程的操作:
3.4.1 multiprocess模塊
3.4.2 multiprocess方法
3.4.3 Process參數介紹:
3.4.4 Process方法介紹

3.4.5 Process屬性介紹
3.4.6 if __name__ ==‘__main__’(特殊 用法)
3.4.7 process類形式開啟進程
3.4.8 聊天並發案例
3.5 內網服務可以做驗證
3.6 socket_server 解決並發
3.7 重點掌握
3.7.1 面向對象開啟子進程傳參
3.8 並發編程難點匯總
第4章 守護進程
4.1 解釋:
4.2 特點:
4.3 用途:
4.4 進程對象的其他方法:terminate,is_alive
4.5 進程對象的其他屬性:pid和name
第5章 同步控制鎖
5.1 multiprocess.Lock
5.2 互斥鎖的特性
第6章 隊列(multiprocess.Queue、multiprocess.Pipe

第1章 ucp協議

1.1 特性

無連接、快、不可靠、面向數據包的傳輸、只能發短消息,可以多個客戶端

1.2 缺陷

mtu最大傳輸數據字節:1500建議(1457),超過該字節也會進行拆分,並且一條沒有發送成功整條數據將丟失

1.3 UDP協議實時通信

server端

import json

import socket

sk = socket.socket(type= socket.SOCK_DGRAM) #設置為udp類型

sk.bind((‘127.0.0.1‘,9090))

while True:

# 不能先發信息

msg,client_addr = sk.recvfrom(1024) # UDP協議接收信息的方法

# {‘msg‘:消息,‘username‘:發送消息的人}

msg_dic = json.loads(msg.decode(‘utf-8‘))

print(‘消息來自%s : %s‘%(msg_dic[‘username‘],msg_dic[‘msg‘]))

msg = input(‘>>>‘)

dic = {‘msg‘:msg,‘username‘:‘server‘}

send_msg = json.dumps(dic).encode(‘utf-8‘)

sk.sendto(send_msg,client_addr)

sk.close()

client端

import json

import socket

server_addr = (‘127.0.0.1‘,9090)

sk = socket.socket(type=socket.SOCK_DGRAM)

while True:

msg = input(‘>>>‘)

dic = {‘msg‘: msg, ‘username‘: ‘client1‘}

send_msg = json.dumps(dic).encode(‘utf-8‘)

sk.sendto(send_msg, server_addr)

msg,server_addr = sk.recvfrom(1024)

msg_dic = json.loads(msg.decode(‘utf-8‘))

print(‘消息來自%s : %s‘ % (msg_dic[‘username‘], msg_dic[‘msg‘]))

sk.close()

第2章 socket的更多方法

2.1 面向鎖的套接字方法

s.setblocking()     設置套接字的阻塞與非阻塞模式
s.settimeout()      設置阻塞套接字操作的超時時間
s.gettimeout()      得到阻塞套接字操作的超時時間

2.1.1 blocking設置非阻塞

import socket

sk =socket.socket()

sk.setblocking(False)

sk.bind((‘127.0.0.1‘,9090))

sk.listen()

#blocking

#setblocking 設置為阻塞

#setblocking(False) 設置為不阻塞

sk.accept()

2.1.2 BlockingIOError

import socket

sk =socket.socket()

sk.setblocking(False)

sk.bind((‘127.0.0.1‘,9090))

sk.listen()

#blocking

#setblocking 設置為阻塞

#setblocking(False) 設置為不阻塞

sk.accept()

#如果設置為不阻塞,那麽沒有消息的時候就會報BlockingIOError

輸出

line 346, in <module>

sk.accept()

File "C:\python3\lib\socket.py", line 205, in accept

fd, addr = self._accept()

BlockingIOError: [WinError 10035] 無法立即完成一個非阻止性套接字操作。

2.2 面向文件的套接字的函數

s.fileno()          套接字的文件描述符
s.makefile()        創建一個與該套接字相關的文件

2.3 驗證客戶端鏈接的合法性

如果你想在分布式系統中實現一個簡單的客戶端鏈接認證功能,又不像SSL那麽復雜,那麽利用hmac+加鹽的方式來實現

服務端

#_*_coding:utf-8_*_

from socket import *

import hmac,os

secret_key=b‘linhaifeng bang bang bang‘

def conn_auth(conn):

‘‘‘

認證客戶端鏈接

:param conn:

:return:

‘‘‘

print(‘開始驗證新鏈接的合法性‘)

msg=os.urandom(32)

conn.sendall(msg)

h=hmac.new(secret_key,msg)

digest=h.digest()

respone=conn.recv(len(digest))

return hmac.compare_digest(respone,digest)

def data_handler(conn,bufsize=1024):

if not conn_auth(conn):

print(‘該鏈接不合法,關閉‘)

conn.close()

return

print(‘鏈接合法,開始通信‘)

while True:

data=conn.recv(bufsize)

if not data:break

conn.sendall(data.upper())

def server_handler(ip_port,bufsize,backlog=5):

‘‘‘

只處理鏈接

:param ip_port:

:return:

‘‘‘

tcp_socket_server=socket(AF_INET,SOCK_STREAM)

tcp_socket_server.bind(ip_port)

tcp_socket_server.listen(backlog)

while True:

conn,addr=tcp_socket_server.accept()

print(‘新連接[%s:%s]‘ %(addr[0],addr[1]))

data_handler(conn,bufsize)

if __name__ == ‘__main__‘:

ip_port=(‘127.0.0.1‘,9999)

bufsize=1024

server_handler(ip_port,bufsize)

客戶端(合法)

#_*_coding:utf-8_*_

__author__ = ‘Linhaifeng‘

from socket import *

import hmac,os

secret_key=b‘linhaifeng bang bang bang‘

def conn_auth(conn):

‘‘‘

驗證客戶端到服務器的鏈接

:param conn:

:return:

‘‘‘

msg=conn.recv(32)

h=hmac.new(secret_key,msg)

digest=h.digest()

conn.sendall(digest)

def client_handler(ip_port,bufsize=1024):

tcp_socket_client=socket(AF_INET,SOCK_STREAM)

tcp_socket_client.connect(ip_port)

conn_auth(tcp_socket_client)

while True:

data=input(‘>>: ‘).strip()

if not data:continue

if data == ‘quit‘:break

tcp_socket_client.sendall(data.encode(‘utf-8‘))

respone=tcp_socket_client.recv(bufsize)

print(respone.decode(‘utf-8‘))

tcp_socket_client.close()

if __name__ == ‘__main__‘:

ip_port=(‘127.0.0.1‘,9999)

bufsize=1024

client_handler(ip_port,bufsize)

客戶端(非法)

#_*_coding:utf-8_*_

__author__ = ‘Linhaifeng‘

from socket import *

def client_handler(ip_port,bufsize=1024):

tcp_socket_client=socket(AF_INET,SOCK_STREAM)

tcp_socket_client.connect(ip_port)

while True:

data=input(‘>>: ‘).strip()

if not data:continue

if data == ‘quit‘:break

tcp_socket_client.sendall(data.encode(‘utf-8‘))

respone=tcp_socket_client.recv(bufsize)

print(respone.decode(‘utf-8‘))

tcp_socket_client.close()

if __name__ == ‘__main__‘:

ip_port=(‘127.0.0.1‘,9999)

bufsize=1024

client_handler(ip_port,bufsize)

第3章 並發編程

3.1 進程特性

l 動態性:進程的實質是程序在多道程序系統中的一次執行過程,進程是動態產生,動態消亡的。

l 並發性:任何進程都可以同其他進程一起並發執行

l 獨立性:進程是一個能獨立運行的基本單位,同時也是系統分配資源和調度的獨立單位;

l 異步性:由於進程間的相互制約,使進程具有執行的間斷性,即進程按各自獨立的、不可預知的速度向前推進

l 結構特征:進程由程序、數據和進程控制塊三部分組成。

l 多個不同的進程可以包含相同的程序:一個程序在不同的數據集裏就構成不同的進程,能得到不同的結果;但是執行過程中,程序不能發生改變。

3.2 並發和並行

概念:

l 並行 : 並行是指兩者同時執行,比如賽跑,兩個人都在不停的往前跑;(資源夠用,比如三個線程,四核的CPU )

l 並發 : 並發是指資源有限的情況下,兩者交替輪流使用資源,比如一段路(單核CPU資源)同時只能過一個人,A走一段後,讓給B,B用完繼續給A ,交替使用,目的是提高效率。

區別:

l 並行是從微觀上,也就是在一個精確的時間片刻,有不同的程序在執行,這就要求必須有多個處理器。

l 並發是從宏觀上,在一個時間段上可以看出是同時執行的,比如一個服務器同時處理多個session。

3.3 同步異步阻塞非阻塞

在了解其他概念之前,我們首先要了解進程的幾個狀態。在程序運行的過程中,由於被操作系統的調度算法控制,程序會進入幾個狀態:就緒,運行和阻塞。

  (1)就緒(Ready)狀態

  當進程已分配到除CPU以外的所有必要的資源,只要獲得處理機便可立即執行,這時的進程狀態稱為就緒狀態。

  (2)執行/運行(Running)狀態當進程已獲得處理機,其程序正在處理機上執行,此時的進程狀態稱為執行狀態。

  (3)阻塞(Blocked)狀態正在執行的進程,由於等待某個事件發生而無法執行時,便放棄處理機而處於阻塞狀態。引起進程阻塞的事件可有多種,例如,等待I/O完成、申請緩沖區不能滿足、等待信件(信號)等。

3.3.1 同步異步

所謂同步就是一個任務的完成需要依賴另外一個任務時,只有等待被依賴的任務完成後,依賴的任務才能算完成,這是一種可靠的任務序列。要麽成功都成功,失敗都失敗,兩個任務的狀態可以保持一致。

  所謂異步是不需要等待被依賴的任務完成,只是通知被依賴的任務要完成什麽工作,依賴的任務也立即執行,只要自己完成了整個任務就算完成了。至於被依賴的任務最終是否真正完成,依賴它的任務無法確定,所以它是不可靠的任務序列。

3.3.2 阻塞與非阻塞

阻塞和非阻塞這兩個概念與程序(線程)等待消息通知(無所謂同步或者異步)時的狀態有關。也就是說阻塞與非阻塞主要是程序(線程)等待消息通知時的狀態角度來說的

3.3.3 小結

l 同步阻塞形式

  效率最低。拿上面的例子來說,就是你專心排隊,什麽別的事都不做。

l 異步阻塞形式

  如果在銀行等待辦理業務的人采用的是異步的方式去等待消息被觸發(通知),也就是領了一張小紙條,假如在這段時間裏他不能離開銀行做其它的事情,那麽很顯然,這個人被阻塞在了這個等待的操作上面;

  異步操作是可以被阻塞住的,只不過它不是在處理消息時阻塞,而是在等待消息通知時被阻塞。

l 同步非阻塞形式

  實際上是效率低下的。

  想象一下你一邊打著電話一邊還需要擡頭看到底隊伍排到你了沒有,如果把打電話和觀察排隊的位置看成是程序的兩個操作的話,這個程序需要在這兩種不同的行為之間來回的切換,效率可想而知是低下的。

l 異步非阻塞形式

  效率更高,

  因為打電話是你(等待者)的事情,而通知你則是櫃臺(消息觸發機制)的事情,程序沒有在兩種不同的操作中來回切換。

3.4 python中進程的操作:

3.4.1 multiprocess模塊

解釋:

multiprocess不是一個模塊而是python中一個操作、管理進程的包。 之所以叫multi是取自multiple的多功能的意思,在這個包中幾乎包含了和進程有關的所有子模塊。由於提供的子模塊非常多,為了方便大家歸類記憶,我將這部分大致分為四個部分:創建進程部分,進程同步部分,進程池部分,進程之間數據共享。

作用:

process模塊是一個創建進程的模塊,借助這個模塊,就可以完成進程的創建

3.4.2 multiprocess方法

Process([group [, target [, name [, args [, kwargs]]]]]),由該類實例化得到的對象,表示一個子進程中的任務(尚未啟動)

l 強調:

1. 需要使用關鍵字的方式來指定參數

2. args指定的為傳給target函數的位置參數,是一個元組形式,必須有逗號

3.4.3 Process參數介紹:

group參數未使用,值始終為None

target表示調用對象,即子進程要執行的任務

args表示調用對象的位置參數元組,args=(1,2,‘egon‘,)

kwargs表示調用對象的字典,kwargs={‘name‘:‘egon‘,‘age‘:18}

name為子進程的名稱

實例:

import os

import time

from multiprocessing import Process

def func2():

print(‘func2 before‘, os.getpid())

time.sleep(5)

print(‘func2 after‘,os.getpid())

def func():

print(‘in func before‘, os.getpid())

time.sleep(3)

print(‘in func after‘,os.getpid())

if __name__ == ‘__main__‘:

p = Process(target=func)

p.daemon = True # 將p設置為一個守護進程

p.start()

p = Process(target=func2)

p.start()

time.sleep(1)

print(‘主進程‘,os.getpid())

3.4.4 Process方法介紹

p.start():啟動進程,並調用該子進程中的p.run()

p.run():進程啟動時運行的方法,正是它去調用target指定的函數,我們自定義類的類中一定要實現該方法

p.terminate():強制終止進程p,不會進行任何清理操作,如果p創建了子進程,該子進程就成了僵屍進程,使用該方法需要特別小心這種情況。如果p還保存了一個鎖那麽也將不會被釋放,進而導致死鎖

p.is_alive():如果p仍然運行,返回True

p.join([timeout]):主線程等待p終止(強調:是主線程處於等的狀態,而p是處於運行的狀態)。timeout是可選的超時時間,需要強調的是,p.join只能join住start開啟的進程,而不能join住run開啟的進程

3.4.5 Process屬性介紹

1 p.daemon:默認值為False,如果設為True,代表p為後臺運行的守護進程,當p的父進程終止時,p也隨之終止,並且設定為True後,p不能創建自己的新進程,必須在p.start()之前設置

2 p.name:進程的名稱

3 p.pid:進程的pid

4 p.exitcode:進程在運行時為None、如果為–N,表示被信號N結束(了解即可)

5 p.authkey:進程的身份驗證鍵,默認是由os.urandom()隨機生成的32字符的字符串。這個鍵的用途是為涉及網絡連接的底層進程間通信提供安全性,這類連接只有在具有相同的身份驗證鍵時才能成功(了解即可)

3.4.6 if __name__ ==‘__main__’(特殊 用法)

在Windows操作系統中由於沒有fork(linux操作系統中創建進程的機制),在創建子進程的時候會自動 import 啟動它的這個文件,而在 import 的時候又執行了整個文件。因此如果將process()直接寫在文件中就會無限遞歸創建子進程報錯。所以必須把創建子進程的部分使用if __name__ ==‘__main__’ 判斷保護起來,import 的時候 ,就不會遞歸運行了

例:

import os,time

from multiprocessing import Process

n =100

def func():

global n

time.sleep(1)

n -= 1

print(‘子進程‘,os.getpid(),os.getppid())

if __name__==‘__main__‘:

print(os.getpid(),os.getppid())

p_lst =[]

for i in range(10):

p = Process(target=func)

p.start()

p_lst.append(p)

for p in p_lst:

p.join()

print(‘主進程‘)

print(‘==>‘,n)

輸出:

C:\python3\python3.exe D:/python/untitled2/python_11/lession.py

8768 7744

子進程 9940 8768

主進程

==> 100

子進程 9860 8768

3.4.7 process類形式開啟進程

除了上面這些開啟進程的方法,還有一種以繼承Process類的形式開啟進程的方式

import os

from multiprocessing import Process

class MyProcess(Process):

def __init__(self,name):

super().__init__()

self.name=name

def run(self):

print(os.getpid())

print(‘%s 正在和女主播聊天‘ %self.name)

if __name__ == ‘__main__‘:

p1=MyProcess(‘wupeiqi‘)

p2=MyProcess(‘yuanhao‘)

p3=MyProcess(‘nezha‘)

p1.start() #start會自動調用run

p2.start()

# p2.run()

p3.start()

p1.join()

p2.join()

p3.join()

print(‘主線程‘)

通過繼承Process類開啟進程

# 進程之間的數據隔離問題

from multiprocessing import Process

def work():

# global n

n=0

print(‘子進程內: ‘,n)

if __name__ == ‘__main__‘:

n = 100

p=Process(target=work)

p.start()

print(‘主進程內: ‘,n)

3.4.8 聊天並發案例

from socket import *

from multiprocessing import Process

server=socket(AF_INET,SOCK_STREAM)

server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)

server.bind((‘127.0.0.1‘,8080))

server.listen(5)

def talk(conn,client_addr):

while True:

try:

msg=conn.recv(1024)

if not msg:break

conn.send(msg.upper())

except Exception:

break

if __name__ == ‘__main__‘: #windows下start進程一定要寫到這下面

while True:

conn,client_addr=server.accept()

p=Process(target=talk,args=(conn,client_addr))

p.start()

使用多進程實現socket聊天並發-server

client端

from socket import *

client=socket(AF_INET,SOCK_STREAM)

client.connect((‘127.0.0.1‘,8080))

while True:

msg=input(‘>>: ‘).strip()

if not msg:continue

client.send(msg.encode(‘utf-8‘))

msg=client.recv(1024)

print(msg.decode(‘utf-8‘))

3.5 內網服務可以做驗證

對並發比較好的都會設置

3.6 socket_server 解決並發

tcp協議下同時開啟多個客戶端通信

3.7 重點掌握

n 進程同步控制——鎖\信號量\事件

(multiprocess.Lock、multiprocess.Semaphore、multiprocess.Event)

n 進程間通信——隊列和管道

(multiprocess.Queue、multiprocess.Pipe)

n 進程間的數據共享——multiproess.Manager

n 進程池和multiprocess.Pool

進程是計算機中資源最小的分配單位

3.7.1 面向對象開啟子進程傳參

import os

import time

from multiprocessing import Process

class MyProcess(Process):

def __init__(self,arg):

super().__init__()

self.arg =arg

def run(self):

print(self.arg)

time.sleep(1)

print(‘in run‘,os.getpid())

if __name__ == ‘__main__‘:

print(‘main:‘ ,os.getpid())

p =MyProcess(123)

p.start()

print(p.is_alive())

# p.terminate()#非阻塞

# print(p.is_alive())

time.sleep(0.01)

print(p.is_alive())

輸出

C:\python3\python3.exe D:/python/untitled2/python_10/server.py

main: 6096

True

True

123

in run 5464

開啟非阻塞方法

if __name__ == ‘__main__‘:

print(‘main:‘ ,os.getpid())

p =MyProcess(123)

p.start()

# print(p.is_alive())

p.terminate()#非阻塞

print(p.is_alive())

輸出

C:\python3\python3.exe D:/python/untitled2/python_10/server.py

main: 6248

True

3.8 並發編程難點匯總

n if __name__ == ‘__main__‘: 在win上必須寫,在其他操作系統上可不寫

n 主進程和子進程的異步 並發效果

n 主進程會等子進程結束之後在結束,為什麽主進程要等待子進程結束

n 在主進程中終止一個子進程

n 開啟多個子進程,多個子進程之間也是異步的

n 多個子進程的join,join會等待子進程結束而結束

n 數據隔離 進程之間的資源 數據都是相互隔離的

n 子進程傳參

n 面向對象的方式開啟子進程

n terminate isalive 方法

第4章 守護進程

4.1 解釋:

進程之間是相互獨立的,主進程代碼運行結束,守護進程隨即終止

4.2 特點:

守護進程會在主進程代碼執行結束後終止

守護進程內無法再開啟子進程,否則拋出異常:AssertionError:daemonic processes are not allowed to have children

4.3 用途:

一般會將占用操作系統的資源歸還回去

註意:進程之間是互相獨立的,主進程代碼運行結束,守護進程隨即終止

例:

import os

import time

from multiprocessing import Process

class Myprocess(Process):

def __init__(self,person):

super().__init__()

self.person = person

def run(self):

print(os.getpid(),self.name)

print(‘%s正在和女主播聊天‘ %self.person)

if __name__ == ‘__main__‘:

p=Myprocess(‘哪咤‘)

p.daemon=True #一定要在p.start()前設置,設置p為守護進程,禁止p創建子進程,並且父進程代碼執行結束,p即終止運行

p.start()

time.sleep(10) # 在sleep時查看進程id對應的進程ps -ef|grep id

print(‘主‘)

# 主進程代碼執行結束守護進程立即結束

import time

from multiprocessing import Process

def foo():

print(123)

time.sleep(1)

print("end123")

def bar():

print(456)

time.sleep(3)

print("end456")

if __name__ == ‘__main__‘:

p1=Process(target=foo)

p2=Process(target=bar)

p1.daemon=True

p1.start()

p2.start()

time.sleep(0.1)

print("main-------")#打印該行則主進程代碼結束,則守護進程p1應該被終止.#可能會有p1任務執行的打印信息123,因為主進程打印main----時,p1也執行了,但是隨即被終止.

輸出

C:\python3\python3.exe D:/python/untitled2/lianxi/1.py

main-------

456

end456

Process finished with exit code 0

4.4 進程對象的其他方法:terminate,is_alive

from multiprocessing import Process

import time

import random

class Myprocess(Process):

def __init__(self,person):

self.name=person

super().__init__()

def run(self):

print(‘%s正在和網紅臉聊天‘ %self.name)

time.sleep(random.randrange(1,5))

print(‘%s還在和網紅臉聊天‘ %self.name)

p1=Myprocess(‘哪咤‘)

p1.start()

p1.terminate()#關閉進程,不會立即關閉,所以is_alive立刻查看的結果可能還是存活

print(p1.is_alive()) #結果為True

print(‘開始‘)

print(p1.is_alive()) #結果為False

4.5 進程對象的其他屬性:pid和name

class Myprocess(Process):

def __init__(self,person):

self.name=person # name屬性是Process中的屬性,標示進程的名字

super().__init__() # 執行父類的初始化方法會覆蓋name屬性

#self.name = person # 在這裏設置就可以修改進程名字了

#self.person = person #如果不想覆蓋進程名,就修改屬性名稱就可以了

def run(self):

print(‘%s正在和網紅臉聊天‘ %self.name)

# print(‘%s正在和網紅臉聊天‘ %self.person)

time.sleep(random.randrange(1,5))

print(‘%s正在和網紅臉聊天‘ %self.name)

# print(‘%s正在和網紅臉聊天‘ %self.person)

p1=Myprocess(‘哪咤‘)

p1.start()

print(p1.pid) #可以查看子進程的進程id

第5章 同步控制鎖

5.1 multiprocess.Lock

解釋:

多個任務可以同時在幾個進程中並發處理,他們之間的運行沒有順序,一旦開啟也不受我們控制。盡管並發編程讓我們能更加充分的利用IO資源

當多個進程使用同一份數據資源的時候,就會引發數據安全或順序混亂問題

例:多進程搶占輸出資源

import os

import time

import random

from multiprocessing import Process

def work(n):

print(‘%s: %s is running‘ %(n,os.getpid()))

time.sleep(random.random())

print(‘%s:%s is done‘ %(n,os.getpid()))

if __name__ == ‘__main__‘:

for i in range(3):

p=Process(target=work,args=(i,))

p.start()

例:使用鎖維護執行順序

# 由並發變成了串行,犧牲了運行效率,但避免了競爭

import os

import time

import random

from multiprocessing import Process,Lock

def work(lock,n):

lock.acquire()

print(‘%s: %s is running‘ % (n, os.getpid()))

time.sleep(random.random())

print(‘%s: %s is done‘ % (n, os.getpid()))

lock.release()

if __name__ == ‘__main__‘:

lock=Lock()

for i in range(3):

p=Process(target=work,args=(lock,i))

p.start()

上面這種情況雖然使用加鎖的形式實現了順序的執行,但是程序又重新變成串行了,這樣確實會浪費了時間,卻保證了數據的安全。

  接下來,我們以模擬搶票為例,來看看數據安全的重要性

例:多進程同時搶購余票

#文件db的內容為:{"count":1}

#註意一定要用雙引號,不然json無法識別

#並發運行,效率高,但競爭寫同一文件,數據寫入錯亂

from multiprocessing import Process,Lock

import time,json,random

def search():

dic=json.load(open(‘db‘))

print(‘\033[43m剩余票數%s\033[0m‘ %dic[‘count‘])

def get():

dic=json.load(open(‘db‘))

time.sleep(0.1) #模擬讀數據的網絡延遲

if dic[‘count‘] >0:

dic[‘count‘]-=1

time.sleep(0.2) #模擬寫數據的網絡延遲

json.dump(dic,open(‘db‘,‘w‘))

print(‘\033[43m購票成功\033[0m‘)

def task():

search()

get()

if __name__ == ‘__main__‘:

for i in range(100): #模擬並發100個客戶端搶票

p=Process(target=task)

p.start()

例:使用鎖來保證數據安全

#文件db的內容為:{"count":5}

#註意一定要用雙引號,不然json無法識別

#並發運行,效率高,但競爭寫同一文件,數據寫入錯亂

from multiprocessing import Process,Lock

import time,json,random

def search():

dic=json.load(open(‘db‘))

print(‘\033[43m剩余票數%s\033[0m‘ %dic[‘count‘])

def get():

dic=json.load(open(‘db‘))

time.sleep(random.random()) #模擬讀數據的網絡延遲

if dic[‘count‘] >0:

dic[‘count‘]-=1

time.sleep(random.random()) #模擬寫數據的網絡延遲

json.dump(dic,open(‘db‘,‘w‘))

print(‘\033[32m購票成功\033[0m‘)

else:

print(‘\033[31m購票失敗\033[0m‘)

def task(lock):

search()

lock.acquire()

get()

lock.release()

if __name__ == ‘__main__‘:

lock = Lock()

for i in range(100): #模擬並發100個客戶端搶票

p=Process(target=task,args=(lock,))

p.start()

5.2 互斥鎖的特性

#加鎖可以保證多個進程修改同一塊數據時,同一時間只能有一個任務可以進行修改,即串行的修改,沒錯,速度是慢了,但犧牲了速度卻保證了數據安全。

雖然可以用文件共享數據實現進程間通信,但問題是:

1.效率低(共享數據基於文件,而文件是硬盤上的數據)

2.需要自己加鎖處理

#因此我們最好找尋一種解決方案能夠兼顧:1、效率高(多個進程共享一塊內存的數據)2、幫我們處理好鎖問題。這就是mutiprocessing模塊為我們提供的基於消息的IPC通信機制:隊列和管道。

隊列和管道都是將數據存放於內存中

隊列又是基於(管道+鎖)實現的,可以讓我們從復雜的鎖問題中解脫出來,

我們應該盡量避免使用共享數據,盡可能使用消息傳遞和隊列,避免處理復雜的同步和鎖問題,而且在進程數目增多時,往往可以獲得更好的可獲展性。

python_11(網絡編程)