多进程

导引

import os
from multiprocessing import Process
def func():
    print('当前进程id:',os.getpid())#获取当前进程的id
    print('当前进程的父进程id:',os.getppid())
    print(1234)

if __name__ == '__main__':
    p=Process(target=func)#将func函数注册到进程中
    #此时创建了一个p进程对象,注意这个p进程是func函数的父进程,
    p.start()#开启了一个子进程
    print('当前进程id:', os.getpid())
    print('当前进程的父进程id:', os.getppid())#p进程的父进程就是pycharm

注意上面的代码是先执行父进程,然后再执行func子进程

func传参数

传递一个参数

import os
from multiprocessing import Process
def func(args):
    print('当前进程id:',os.getpid())#获取当前进程的id
    print('当前进程的父进程id:',os.getppid())
    print(1234)
    print(args)

if __name__ == '__main__':
    p=Process(target=func,args=(1,))#将func函数注册到进程中,注意args传递的参数是一个元组,因为1只是一个数字,所以后面必须加逗号
    #此时创建了一个p进程对象,注意这个p进程是func函数的父进程,
    p.start()#开启了一个子进程
    print('当前进程id:', os.getpid())
    print('当前进程的父进程id:', os.getppid())#p进程的父进程就是pycharm

传递2个参数

import os
from multiprocessing import Process
def func(args,args1):
    print('当前进程id:',os.getpid())#获取当前进程的id
    print('当前进程的父进程id:',os.getppid())
    print(1234)
    print(args,args1)

if __name__ == '__main__':
    p=Process(target=func,args=(1,2))#将func函数注册到进程中,注意args传递的参数是一个元组,因为1只是一个数字,所以后面必须加逗号
    #此时创建了一个p进程对象,注意这个p进程是func函数的父进程,
    p.start()#开启了一个子进程
    print('当前进程id:', os.getpid())
    print('当前进程的父进程id:', os.getppid())#p进程的父进程就是pycharm

join的用法

from multiprocessing import Process
def func(args1,args2):
    print(args1)
    print(args2)

if __name__=='__main__':
    p=Process(target=func,args=(10,20))
    p.start()
    print('主程序运行结束')

在上面的代码中依然是先执行主进程,然后再执行func子进程,其本质是两个进程是异步执行

那么使用join后可以将jion之后的进程改为同步执行,即先执行子进程然后执行主进程,那么join后面的主进程代码在子进程之后执行。

from multiprocessing import Process
def func(args1,args2):
    print(args1)
    print(args2)

if __name__=='__main__':
    p=Process(target=func,args=(10,20))
    p.start()
    print('AAAA')
    p.join()
    print('主程序运行结束')

D:\anoconda\python.exe F:/python/python学习/人工智能/第一阶段day2/sever.py
AAAA
10
20
主程序运行结束

Process finished with exit code 0

开启多个子进程

方案一:

法一:将p.start多写几遍

from multiprocessing import Process
def func(args1,args2):
    print(args1)
    print(args2)

if __name__=='__main__':
    p1=Process(target=func,args=(10,20))
    p1.start()
    p2 = Process(target=func, args=(10, 20))
    p2.start()
    p3 = Process(target=func, args=(10, 20))
    p3.start()
    p4 = Process(target=func, args=(10, 20))
    p4.start()

法二:for循环

from multiprocessing import Process
def func(args1,args2):
    print(args1)
    print(args2)

if __name__=='__main__':
    for i in range(4):
        p=Process(target=func,args=(10,20))
        p.start()

再加上join方法的体验

from multiprocessing import Process
def func(args1,args2):
    print(args1)
    print(args2)

if __name__=='__main__':
    for i in range(4):
        p=Process(target=func,args=(10*i,20*i))
        p.start()
        

上面的代码中没有join那么代码是异步执行,如果多执行几次就会发现,每次执行的结果不太一样。这正是由于异步进程导致的

如果加上join,那么就会变成同步进程,此时无论执行多少次每次代码的执行顺序完全一样且能看出明显的执行顺序。

from multiprocessing import Process
def func(args1,args2):
    print(args1)
    print(args2)

if __name__=='__main__':
    for i in range(4):
        p=Process(target=func,args=(10*i,20*i))
        p.start()
        p.join()

进阶

from multiprocessing import Process
def func(args1,args2):
    print(args1)
    print(args2)

if __name__=='__main__':
    for i in range(4):
        p=Process(target=func,args=(10*i,20*i))
        p.start()
    p.join()
    print('执行结束')

注意自己上面的代码是每次都是子进程执行完以后才会执行print('执行结束'),但是老师的代码则出现不稳定现象,也就是说有可能print('执行结束')会在子进程执行的过程中被执行。

老师通过下面的代码确保print('执行结束')每次都会在子进程结束之后运行

from multiprocessing import Process
def func(args1,args2):
    print(args1)
    print(args2)

if __name__=='__main__':
    p_list=[]
    for i in range(4):
        p=Process(target=func,args=(10*i,20*i))
        p_list.append(p)
        p.start()
    [p.join() for p in p_list]
    print('执行结束')

实际应用,比如要先在4个文件中写入内容,然后读出文件所在的位置。

注意这里需要用到异步进程:

因为在向4个文件中写入内容的这个过程可以采用异步进程同步执行,这样写入的过程可以是无序的,但是这样节省时间,然后当向所有文件中的内容写入完毕后再来读出文件所在位置这就有明显的先后顺序,先写入内容然后再执行读,这就需要同步进程用到join

下面是自己的代码,感觉能够实现老师所需要的目的

from multiprocessing import Process
import os
def func(filename,content):
    with open(filename,'w')as f:
        f.write(content)
if __name__=='__main__':
    # p_list=[]
    for i in range(4):
        p=Process(target=func,args=('info%s'%i,str(i)))
        # p_list.append(p)
        p.start()

    # [p.join() for p in p_list]
    p.join()
    print([i for i in os.walk('F:/python/python学习/人工智能/第一阶段day2')])

下面是老师的代码

from multiprocessing import Process
import os
def func(filename,content):
    with open(filename,'w')as f:
        f.write(content)
if __name__=='__main__':
    p_list=[]
    for i in range(4):
        p=Process(target=func,args=('info%s'%i,str(i)))
        p_list.append(p)
        p.start()

    [p.join() for p in p_list]
    print([i for i in os.walk('F:/python/python学习/人工智能/第一阶段day2')])

自己再修改下代码:实现先向4个文件中分别写入内容,然后再将文件中的内容读出来

from multiprocessing import Process
import os
def func(filename,content):
    with open(filename,'w')as f:
        f.write(content)
if __name__=='__main__':
    # p_list=[]
    for i in range(4):
        p=Process(target=func,args=('info%s'%i,str(i)))
        # p_list.append(p)
        p.start()
    p.join()
    # [p.join() for p in p_list]
    for i in range(4):
        with open('info%s'%i,'r')as f:
            h=f.read()
            print('文件名:'+'info%s'%i,'内容是:'+h)

方案二:

from multiprocessing import Process
import os
class Myprocess(Process):
    def run(self):
        print(os.getpid())

if __name__=='__main__':
    print('主进程:',os.getpid())
    p1=Myprocess()
    p1.start()
    p2 = Myprocess()
    p2.start()

自定义类:继承Process类
必须实现一个run方法,run方法中是再子进程中执行的代码

那么如何传参;

from multiprocessing import Process
import os
class Myprocess(Process):
    def __init__(self,arg1,arg2):
        super().__init__()#注意这里必须调用的是Myprocess这个父类,所以需要用到super方法
        self.arg1=arg1
        self.arg2 = arg2

    def run(self):
        print(os.getpid())
        print(self.name)
        print(self.arg1)
        print(self.arg2)

if __name__=='__main__':
    print('主进程:',os.getpid())
    p1=Myprocess(1,2)
    p1.start()
    p2 = Myprocess(1,2)
    p2.start()

多进程之间的数据隔离问题

from multiprocessing import Process
import os
def func():
    global n#将n定义为一个全局变量
    n=0
    print('pid:%s'%os.getpid(),n)

if __name__=='__main__':
    p=Process(target=func)
    n=100
    p.start()
    print('pid:%s'%os.getpid(),n)

D:\anoconda\python.exe F:/python/python学习/人工智能/第一阶段day2/sever.py
pid:12160 100
pid:6328 0

Process finished with exit code 0

如上结果所示:按照一般理解;定义了全局变量以后,主进程中的n应该是0,但是实际上却是100;但实际上定义的这个全局变量位于子进程中,而子进程和主进程在不借助外界手段的情况下双方之间是不能相互传递参数。

如果加上join

from multiprocessing import Process
import os
def func():
    global n#将n定义为一个全局变量
    n=0
    print('pid:%s'%os.getpid(),n)

if __name__=='__main__':
    p=Process(target=func)
    n=100
    p.start()
    p.join()
    print('pid:%s'%os.getpid(),n)

D:\anoconda\python.exe F:/python/python学习/人工智能/第一阶段day2/sever.py
pid:11540 0
pid:9680 100

Process finished with exit code 0

结果依然是主进程和子进程之间的的n不一致,说明在主进程和子进程之间运行过程中,在内存中双方之间是相互隔离的

使用多进程实现socket服务的并发效果

sever端

import socket
from multiprocessing import Process

def serve(conn):
    ret='你好'.encode('utf-8')
    conn.send(ret)
    msg = conn.recv(1024).decode('utf-8')
    print(msg)

if __name__ == '__main__':
    sk = socket.socket()
    sk.bind(('127.0.0.1', 8080))
    sk.listen()
    while 1:
        conn, addr = sk.accept()
        p=Process(target=serve,args=(conn,))
        p.start()
        conn.close()
    sk.close()

client1和client2一直到clientn代码:

import socket
sk=socket.socket()
sk.connect(('127.0.0.1',8080))

msg=sk.recv(1024).decode('utf-8')
print(msg)
msg1=input('>>>:').encode('utf-8')
sk.send(msg1)

sk.close()

这样通过以上代码实现了在tcp协议下,一个sever端连接多个client端。

 守护进程的几种常用方法:

导引

from multiprocessing import Process
import time

def func():
    while 1:
        time.sleep(0.5)
        print('我还活着')

if __name__=='__main__':
    p=Process(target=func)
    p.start()
    while 1:
        print('我是socket')
        time.sleep(2)

修改主进程

from multiprocessing import Process
import time

def func():
    while 1:
        time.sleep(0.5)
        print('我还活着')

if __name__=='__main__':

    p=Process(target=func)
    p.start()
    i=0
    while i<10:
        print('我是socket')
        time.sleep(1)
        i=i+1

在上面的代码中,主进程虽然结束,但是子进程依然在继续执行。那么如果需要当主进程结束的时候,子进程就需要结束怎么做?

from multiprocessing import Process
import time

def func():
    while 1:
        time.sleep(0.5)
        print('我还活着')

if __name__=='__main__':
    p=Process(target=func)
    p.daemon=True#将子进程设置为守护进程
    p.start()
    i=0
    while i<5:
        print('我是socket')
        time.sleep(1)
        i=i+1

守护进程的特点:守护进程会随着主进程的代码执行的结束而结束

from multiprocessing import Process
import time

def func():
    while 1:
        time.sleep(0.5)
        print('我还活着')

def func2():
    print('in func2')
    time.sleep(8)
    print('in func2 finished')

if __name__=='__main__':
    p=Process(target=func)
    p.daemon=True#将子进程设置为守护进程
    p.start()
    Process(target=func2).start()
    i=0
    while i<3:
        print('我是socket')
        time.sleep(1)
        i=i+1

如上代码所示,func是守护进程,当主进程3秒结束后,func就结束。但是func2此时尚未结束,所以程序还会等待5秒后才会全部结束。也就是说守护进程func的是随着主进程的结束而结束,与其他进程是否结束无关。

进阶:

from multiprocessing import Process
import time

def func():
    while 1:
        time.sleep(0.5)
        print('我还活着')

def func2():
    print('in func2')
    time.sleep(8)
    print('in func2 finished')

if __name__=='__main__':
    p=Process(target=func)
    p.daemon=True#将子进程设置为守护进程
    p.start()
    p1=Process(target=func2)
    p1.start()
    p1.terminate()#代码向操作系统发起要求结束进程
    print(p1.is_alive())#但是此时操作系统的响应还需要一定时间,所以这里的结果是true
    print(p1.name)#当前进程的名字
    time.sleep(1)#当经过1秒的延迟后
    print(p1.is_alive())#这里的结果就是False了

进程锁:

以从12306网站买票为例:

首先在pycharm中创建一个名为ticket的txt文件,文件内容:{"ticket":1},关键点,由于是txt文件,所以这里的"ticket"必须是双引号,不能是单引号,否则在引用json的时候会报错

from multiprocessing import Process
import json

def func(i):
    with open('ticket') as f:
        dic=json.load(f)
    print('还有%s张余票'%dic['ticket'])

if __name__=='__main__':
    for i in range(10):
        p=Process(target=func,args=(i,))
        p.start()

进阶

from multiprocessing import Process
import json
import time

def show(i):#查询余票
    with open('ticket') as f:
        dic=json.load(f)
    print('余票 %s'%dic['ticket'])
#模拟买票的过程
def buy_ticket(i):
    with open('ticket') as f:
        dic=json.load(f)
        time.sleep(0.1)#买票的时候也需要查询,这里是模拟查询的延迟
    if dic['ticket']>0:
        dic['ticket']-=1
        print('\033[32m%s买到票了\033[0m'%i)
    else:
        print('\033[31m%s没有买到票\033[0m'%i)
    time.sleep(0.1)#买到票以后,给购票系统一个反馈也需要时间,所以这里需要0.1秒的延迟,由于向ticket中写入需要时间,所以存在一定的延迟,但是此时系统依然在执行买票这个程序,所以就出现了虽然只剩下一张票,但是却出现了多人买到的现象
    with open('ticket','w') as f:
        json.dump(dic,f)

if __name__=='__main__':
    for i in range(10):
        p=Process(target=show,args=(i,))
        p.start()
    for i in range(10):
        p=Process(target=buy_ticket,args=(i,))
        p.start()

所得概念:当给一段代码加上锁以后,只能被一个进程来执行,此时其他进程需要排队,当第一个进程执行完以后其他进程才能继续执行。可以形象得比喻为给那段代码加上了一把锁。只有拥有钥匙的进程才能取执行被加锁的代码

from multiprocessing import Process
import json
import time
from multiprocessing import Lock

def show(i):#查询余票
    with open('ticket') as f:
        dic=json.load(f)
    print('余票 %s'%dic['ticket'])

#模拟买票的过程
def buy_ticket(i,lock):
    lock.acquire()#拿到钥匙
    with open('ticket') as f:
        dic=json.load(f)
        time.sleep(0.1)#买票的时候也需要查询,这里是模拟查询的延迟
    if dic['ticket']>0:
        dic['ticket']-=1
        print('\033[32m%s买到票了\033[0m'%i)
    else:
        print('\033[31m%s没有买到票\033[0m'%i)
    time.sleep(0.1)#买到票以后,给购票系统一个反馈也需要时间,所以这里需要0.1秒的延迟,由于向ticket中写入需要时间,所以存在一定的延迟,但是此时系统依然在执行买票这个程序,所以就出现了虽然只剩下一张票,但是却出现了多人买到的现象
    with open('ticket','w') as f:
        json.dump(dic,f)
    lock.release()#归还钥匙

if __name__=='__main__':
    for i in range(10):
        p=Process(target=show,args=(i,))
        p.start()
    lock=Lock()
    for i in range(10):
        p=Process(target=buy_ticket,args=(i,lock))
        p.start()

那么加锁的意义在于:当加锁后代码由异步执行变为同步执行,但是却有效保证了数据的安全性,也就是说虽然牺牲了效率,但是保证了数据的安全性。

 多进程的补充

from multiprocessing import Process

def func():
    num=input('>>>')


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

如上代码所示,在运行后会报错。原因是因为主进程运行后会在内存中重新开辟一块空间来作为子进程运行的空间,两个进程之间不在同一个内存空间中,所以会报错。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2019-04-03 23:39  舒畅123  阅读(113)  评论(0编辑  收藏  举报