并发编程

一、进程基本操做

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
相关文章
相关标签/搜索
本站公众号
   欢迎关注本站公众号,获取更多信息