进程

目录

  • 代码创建进程

  • join方法

  • 进程间数据默认隔离

  • 进程对象属性和方法

  • 僵尸进程与孤儿进程

  • 守护进程

  • 互斥锁

内容

代码创建进程

创建进程的方式

  1. 鼠标双击桌面一个应用图标
  2. 代码创建

创建进程的方式

在内存中申请一块内存空间用于运行相应的程序代码

代码创建进程的两种方式

第一种:函数

from multiprocessing import Process
import time

def task(name):
    print(f'{name} is running')
    time.sleep(3)
    print(f'{name} is over')

if __name__ == '__main__':
    p = Process(target=task,args=('zhou',)) # 创建一个进程对象
    p.start() # 告诉操作系统创建一个新的进程
    print('主进程')
View Code

打印结果为:

主进程
zhou is running
zhou is over
View Code

第二种:面向对象

from multiprocessing import Process
import time

class MyProcess(Process):
    def __init__(self,username):
        self.username = username
        super().__init__()
    def run(self):
        print('hello',self.username)
        time.sleep(3)
        print('hi',self.username)

if __name__ == '__main__':
    p = MyProcess('zhou')
    p.start()
    print('主进程')
View Code

打印结果为:

主进程
hello zhou
hi zhou
View Code

进程实现并发

服务端:

import socket

server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)

while True: # 链接循环
    sock,add = server.accept()
    while True: # 通信循环
        data = sock.recv(1024) # 不考虑粘包问题
        print(data.decode('utf8'))
        sock.send(data.upper()) # 客户端发一个消息就立马回复一个消息的大写
View Code

客户端:

import socket

client = socket.socket()
client.connect(('127.0.0.1',8080))

while True:
    client.send(b'hello')
    data = client.recv(1024)
    print(data.decode('utf8'))
View Code

多个客户端同时运行只能运行一个,服务端只能与一个进行交互

方法:将服务客户端的代码封装成函数(通信代码),并创建进程

服务端:

import socket
from multiprocessing import Process

server = socket.socket()
server.bind(('127.0.0.1',8989))
server.listen(5)

def task(sock):
    while True: # 通信循环
        data = sock.recv(1024) # 不考虑粘包问题
        print(data.decode('utf8'))
        sock.send(data.upper()) # 客户端发一个消息就立马回复一个消息的大写

if __name__ == '__main__':

    while True: # 链接循环
        sock,add = server.accept()
        p = Process(target=task,args=(sock,))
        p.start()
View Code

join方法

from multiprocessing import Process
import time

def task(name):
    print(f'{name} is running')
    time.sleep(3)
    print(f'{name} is over')

if __name__ == '__main__':
    p = Process(target=task,args=('zhou',))
    p.start()
    print('主进程')
View Code

基于上述代码产生需求

需求:想让p.start()之后的代码,等待子进程全部运行结束之后再打印

这里就用到了join方法:

from multiprocessing import Process
import time

def task(name):
    print(f'{name} is running')
    time.sleep(3)
    print(f'{name} is over')

if __name__ == '__main__':
    p = Process(target=task,args=('zhou',))
    p.start()
    p.join() # 等待子进程运行结束之后再往下执行
    print('主进程')
View Code

如果子进程的睡眠时间是 sleep(n),并且有三个进程:

from multiprocessing import Process
import time

def task(name,n):
    print(f'{name} is running')
    time.sleep(n)
    print(f'{name} is over')

if __name__ == '__main__':
    p1 = Process(target=task, args=('zhou', 1))
    p2 = Process(target=task, args=('zhou', 2))
    p3 = Process(target=task, args=('zhou', 3))
    start_time = time.time()
    p1.start()
    p2.start()
    p3.start()
    p1.join()
    p2.join()
    p3.join()
    end_time = time.time() - start_time
    print('主进程',f'总耗时:{end_time}')
View Code

请问总耗时大概是几秒?

结果:

zhou is running
chen is running
jin is running
zhou is over
chen is over
jin is over
主进程 总耗时:3.1336801052093506
View Code

把上述代码修改一下:

from multiprocessing import Process
import time

def task(name,n):
    print(f'{name} is running')
    time.sleep(n)
    print(f'{name} is over')

if __name__ == '__main__':
    p1 = Process(target=task, args=('zhou', 1))
    p2 = Process(target=task, args=('chen', 2))
    p3 = Process(target=task, args=('jin', 3))
    start_time = time.time()
    p1.start()
    p1.join()
    p2.start()
    p2.join()
    p3.start()
    p3.join()
    end_time = time.time() - start_time
    print('主进程',f'总耗时:{end_time}')
View Code

那这次又是多少秒呢?

结果:

zhou is running
zhou is over
chen is running
chen is over
jin is running
jin is over
主进程 总耗时:6.378488302230835
View Code

进程间数据默认隔离

内存可以看成是有很多个小隔间组成的,彼此不干扰

from multiprocessing import Process

money = 999
def task():
    global money # 局部修改全局不可变类型
    money = 666

if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    p.join()
    print(money)
View Code

打印结果是:999

而子进程的money是多少呢?

from multiprocessing import Process

money = 999
def task():
    global money # 局部修改全局不可变类型
    money = 666
    print('子进程',money)

if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    p.join()
    print(money)
View Code

结果为:

子进程 666
999

进程对象属性和方法

查看进程号

  1. windows终端: tasklist      进程号:PID
  2. mac终端:ps  -ef     进程号:PID

代码查看进程号

获取进程号的用处之一:通过代码的方式管理进程

  1. current_process函数
    from multiprocessing import Process,current_process
    print(current_process().pid)
    View Code

    这里会拿到一个进程号,加上time模块就可以在拿到进程号之前在终端查看到该进程号

  2. os 模块
    import os
    
    def task():
        print('子进程号:',os.getpid())
        print('父进程号:',os.getppid())
    
    if __name__ == '__main__':
        print('主进程号:',os.getpid())
        print('主主进程:',os.getppid())
        p = Process(target=task)
        p.start()
    View Code

    os.getpid()  获取当前进程的进程号 

             os.getppid() 获取当前进程的父进程号

杀死进程号

  1. 命令行:

    windows: taskkill关键字 ; mac/linux: kill关键字

  2. 代码: terminate()
    from multiprocessing import Process,current_process
    import os
    
    def task():
        print('子进程号:',os.getpid())
        print('父进程号:',os.getppid())
    
    if __name__ == '__main__':
        p = Process(target=task)
        p.start()
        p.terminate()
        print('主进程')
    View Code

    结果:主进程

判断子进程是否存活

is_alive()

import os

def task():
    print('子进程号:',os.getpid())
    print('父进程号:',os.getppid())

if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    p.terminate()
    print(p.is_alive())
    print('主进程')
View Code

结果为:

True
主进程

布尔值为True的原因:terminate告诉操作系统杀死子进程需要一点时间

import os
import time

def task():
    print('子进程号:',os.getpid())
    print('父进程号:',os.getppid())

if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    p.terminate()
    time.sleep(0.1)
    print(p.is_alive())
    print('主进程')
View Code

结果为:

False
主进程

僵尸进程与孤儿进程

僵尸进程

所有的子进程在运行结束之后都会变成僵尸进程(死了但是没有死透)

还保留着pid和一些运行过程的中的记录便于主进程查看(短时间保存)

这些信息会被主进程回收(僵尸彻底死了)

孤儿进程

子进程存活者,父进程意外死亡

子进程会被操作系统自动接管

守护进程

守护:死活全部参考守护的对象,对象死立刻死

from multiprocessing import Process
import time

def task(name):
    print(f'{name}还活着')
    time.sleep(3)
    print(f'{name}死了')

if __name__ == '__main__':
    p = Process(target=task,args=('zhou',))
    p.daemon = True # 将子进程设置为守护进程:主进程结束,子进程立刻结束
    p.start()
    print('chen')
View Code

结果为:chen 

如果给主进程加一秒

from multiprocessing import Process
import time

def task(name):
    print(f'{name}还活着')
    time.sleep(3)
    print(f'{name}死了')

if __name__ == '__main__':
    p = Process(target=task,args=('zhou',))
    p.daemon = True # 将子进程设置为守护进程:主进程结束,子进程立刻结束
    p.start()
    time.sleep(1)
    print('chen')
View Code

结果为:

zhou还活着
chen

使用场景:只要自己结束了,守护的程序也直接结束,不继续占用资源了

模拟抢票程序

import json
from multiprocessing import Process
import time
import random

# 查票
def check(name):
    with open(r'ticket_data.json','r',encoding='utf8') as f:
        data = json.load(f)
    print(f'{name}查询当前余票:%s'%data.get('ticket_num'))

# 买票
def buy(name):
    with open(r'ticket_data.json','r',encoding='utf8') as f:
        data = json.load(f)
    time.sleep(random.randint(1,3))
    # 判断是否还有余票
    if data.get('ticket_num') > 0:
        data['ticket_num'] -= 1
        with open(r'ticket_data.json','w',encoding='utf8') as f:
            json.dump(data,f)
        print(f'{name}抢票成功')
    else:
        print(f'{name}抢票失败,没有余票了')

def run(name):
    check(name)
    buy(name)

if __name__ == '__main__':
    for i in range(10):
        p = Process(target=run,args=('用户:%s'%i,))
        p.start()
View Code

结果为:

用户:0查询当前余票:4
用户:1查询当前余票:4
用户:2查询当前余票:4
用户:3查询当前余票:4
用户:4查询当前余票:4
用户:5查询当前余票:4
用户:6查询当前余票:4
用户:8查询当前余票:4
用户:7查询当前余票:4
用户:9查询当前余票:4
用户:8抢票成功
用户:1抢票成功
用户:2抢票成功
用户:3抢票成功
用户:4抢票成功
用户:7抢票成功
用户:0抢票成功
用户:6抢票成功用户:5抢票成功

用户:9抢票成功
View Code

互斥锁

刚才的抢票程序可以看出:当多个进程操作同一份数据的时候会造成数据的错乱

这个时候需要加锁处理(互斥锁)

将并发变成串行,虽然牺牲了效率但是保证了数据的安全

互斥锁在主进程中使用

互斥锁不能轻易使用,容易造成死锁现象

互斥锁只在处理数据的部分加锁,不能什么地方都加,严重影响程序的效率

正确的抢票程序:

import json
from multiprocessing import Process,Lock
import time
import random

# 查票
def check(name):
    with open(r'ticket_data.json','r',encoding='utf8') as f:
        data = json.load(f)
    print(f'{name}查询当前余票:%s'%data.get('ticket_num'))

# 买票
def buy(name):
    with open(r'ticket_data.json','r',encoding='utf8') as f:
        data = json.load(f)
    time.sleep(random.randint(1,3))
    # 判断是否还有余票
    if data.get('ticket_num') > 0:
        data['ticket_num'] -= 1
        with open(r'ticket_data.json','w',encoding='utf8') as f:
            json.dump(data,f)
        print(f'{name}抢票成功')
    else:
        print(f'{name}抢票失败,没有余票了')

def run(name,mutex):
    check(name)
    # 只需要把买票环节变成串行即可
    mutex.acquire() # 抢锁
    buy(name)
    mutex.release() # 放锁

if __name__ == '__main__':
    mutex = Lock()
    for i in range(1,10):
        p = Process(target=run,args=('用户:%s'%i,mutex))
        p.start()
View Code

锁的相关知识:

行锁:针对行数据加锁,同一时间只能一个人操作

表锁:针对表数据加锁,同一时间只能一个人操作

锁的应用范围很广,但是核心都是为了保证数据的安全

          

posted @ 2022-04-19 19:11  顺溜_7  阅读(71)  评论(0编辑  收藏  举报