python_11(网络编程)

第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通信机制:队列和管道。

队列和管道都是将数据存放于内存中

队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来,

我们应该尽量避免使用共享数据,尽可能使用消息传递和队列,避免处理复杂的同步和锁问题,而且在进程数目增多时,往往可以获得更好的可获展性。

posted @ 2018-08-03 11:27  王晓冬  阅读(252)  评论(0编辑  收藏  举报