进程微讲解

进程微讲解

1.代码创建进程

# 创建进程的方式
	我们前面已经讲过进程就是正在执行的代码或者说正在运行的程序,那么创建进程就有两种方式。第一种就是当我们用鼠标
双击桌面的一个应用图标时,就相当于在创建一个进程,我们知道当我们双击一个应用图标就是想要启动这个程序,双击之后就会控
制硬盘转动,把硬盘中的数据加载到内存中,然后CPU再执行内存中的任务,其实我们加载到内存中的就是代码,CPU执行任务其实就
是执行代码;第二种就是代码创建一个进程,其实还是CPU执行代码产生进程。创建进程的本质就是在内存空间中申请一块内存空间用
于运行相应的程序代码。
# 用代码创建进程的第一种方式
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=('oscar',))  # 创建一个进程
    p.start()  # 告诉操作系统创建一个新的进程
    print('主进程')
    
# 用代码创建进程的第二种方式
from multiprocessing import Process
import time

class MyProcess(Process):
    def __init__(self,name):
        self.name = name
        super().__init__()

    def run(self):
        print('刚刚来的',self.name)
        time.sleep(3)
        print('又走了',self.name)

if __name__ == '__main__':
    p = MyProcess('oscar')
    p.start()
    print('主进程')
    
# 不同系统下代码创建进程的方式
windows系统:创建进程是以导入模块的方式进行的,所以创建进程的代码必须写在__main__子代码中,否则就会直接报错,一直在无
限的创建进程。
linux、mac系统:创建进程直接拷贝一份源码然后执行即可,不需要写在__main__子代码中。

2.join方法

# 1.join方法的含义及简单用法
	1.join方法可以让主进程代码等待子进程代码运行完毕之后在执行。我们在上述创建进程的时候,执行打印的顺序是先打印
的主进程的,然后在打印子进程的,我们使用join方法之后就可以先执行子进程的代码,子进程的代码执行完毕之后在执行主进程的代码。
    2.简单用法
    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=('oscar',))  # 创建一个进程
        p.start()  # 告诉操作系统创建一个新的进程
        p.join()  # 执行完子进程的代码之后在执行下面的主进程代码
        print('主进程')
        
# 2.进一步理解进程的等待过程
from multiprocessing import Process
import time

def task(name,n):
    print(f'{name}刚刚来')
    time.sleep(n)
    print(f'{name}又走了')

if __name__ == '__main__':
    p1 = Process(target=task,args=('oscar',1))
    p2 = Process(target=task,args=('kevin',4))
    p3 = Process(target=task,args=('jason',3))
    start_time = time.time()
    p1.start()
    p2.start()
    p3.start()
    p1.join()
    p2.join()
    p3.join()
    end_time = time.time() - start_time
    print(end_time)  # 4.111866235733032

    # start_time = time.time()
    # p1.start()
    # p1.join()
    # p2.start()
    # p2.join()
    # p3.start()
    # p3.join()
    # end_time = time.time() - start_time
    # print(end_time)  # 8.277277231216431
'''当start与join不是交替执行的时候,总耗时就是最长的一个时间,因为当它在等待的时候其他的也在运行,而当start与join
交替执行的时候,总耗时就是所有时间的综合,因为交替执行的情况下,一个进程在等待,其他的也不会执行,等这一个执行完之后
才会执行下一个'''

3.进程间数据默认隔离

	我们知道当我们创建一个进程的时候它是有一个独自的内存空间的,这些内存空间是彼此互不干扰的,也就是相互隔离,谁
也不碰谁,默认隔离,但是也是可以通过一些手段打破隔离的,这里我们先不讲,我们先验证一下进程间的数据是不是默认隔离的。
    from multiprocessing import Process

    s = 1

    def task():
        global s  # 局部修改全局不可变类型
        s = 2

    if __name__ == '__main__':
        p = Process(target=task)
        p.start()
        p.join()  # 确保子进程代码运行结束在打印s
        print(s)  # 1

4.进程对象属性和方法

# 查看进程号
1.在cmd命令行中
	Windows:tasklist
    mac:ps -ef
2.在pycharm中
	(1).current_process函数
	from multiprocessing import Process,current_process
	print(current_process().pid)  # 14320
    (2).os模块
    from multiprocessing import Process
import os


def task():
    print(os.getpid())  # 获取当前进程的进程号
    print(os.getppid())  # 获取当前进程的父进程号

if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    p.join()
    
# 获取进程号的用处之一就是可以通过代码的方式管理进程
1.杀死子进程
	(1).cmd命令行中
	windows系统:tsskkill -PID 进程号 -f
	mac系统:kill -9 PID
	(2).pycharm中
    terminate()
2.判断子进程是否存活
	is_alive()  # 子进程存活返回True,不存活就返回False
# 通过代码管理进程
from multiprocessing import Process
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())  # False
    print('主进程')

5.僵尸进程与孤儿进程

# 僵尸进程
	主进程默认需要等待子进程结束才会结束,所有的子进程在运行结束之后都会变成僵尸进程,意思就是虽然已经执行结
束了,但是还没有回收,还保留着pid和一些运行过程中的记录,便于主进程查看,只是短时间的保存,然后这些信息就会被主进
程回收,子进程也就彻底结束了。
    结束僵尸进程的两种方式:
        1.主进程正常结束
        2.调用join方法
# 孤儿进程
	子进程存活着,父进程意外死亡,这时候子进程就是孤儿进程,这些孤儿进程就会被操作系统自动接管。

6.守护进程

	当一个进程守护另一进程,被守护的进程一旦死亡,那么守护该进程的进程也就会立刻死亡。
    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=('tom',))
        # 必须写在start前面
        p.daemon = True  # 将子进程设置为守护进程:主进程一结束,子进程立马结束
        p.start()
        print('主进程死亡')  # 只打印了主进程的信息,然后主进程就结束了,所以子进程立马死亡,不会执行

7.互斥锁

	在讲'互斥锁'之前我们现讲一个现象,过年回家的时候大家一定都抢过车票吧,有时候手机上明明显示有票,但是点击
购买的时候却提示没有票了,然后我们回去查看车票的时候,发现确实没有车票了。其实这个现象的原因是这样的,当我们进入这
个页面的时候,程序会反馈给我们余票情况,当我们在查看余票的时候,其实其他人已经在购买当中了,所以呢只要你不刷新或者
不点击下一步,那么那个页面的数据是不会改变的,但是只要你一刷新,就会刷新数据,更新为当前的余票状况。
# 代码模拟抢票
    import json
    from multiprocessing import Process
    import time
    import random

    # 查票
    def search(name):
        with open(r'piao.json','r',encoding='utf8') as f:
            data = json.load(f)
        print('%s查询当前余票为:%s'%(name,data['num']))

    # 买票
    def buy(name):
        # 买票之前再次查票,因为期间其他人可能已经把票买走了
        with open(r'piao.json', 'r', encoding='utf8') as f:
            data = json.load(f)
        time.sleep(random.randint(1,3))  # 模仿手机网络延迟
        # 判断是否还有余票
        if data['num'] > 0:
            data['num'] -= 1
            with open(r'piao.json', 'w', encoding='utf8') as f1:
                json.dump(data,f1)
            print(f'{name}抢票成功')
        else:
            print(f'{name}抢票失败')

    def run(name):
        search(name)
        buy(name)

    # 模拟多人同时抢票
    if __name__ == '__main__':
        for i in range(1,10):
            p = Process(target=run,args=(i,))
            p.start()
            
'''上述模拟抢票的代码是有问题的,只有一张票每个人都抢到了,这是不符合实际的,所以呢就用到了互斥锁。'''
# 互斥锁
	当多个进程操作同一份数据的时候会造成数据的错乱,这个时候就需要加锁处理(互斥锁),将并发改为串行,虽然牺
牲了效率但是保证了数据的安全。但是呢互斥锁并不能轻易使用,容易造成死锁现象,互斥锁只在处理数据的部分加锁,不能什
么地方都加锁,否则就严重影响到了程序的效率。
    所以呢我们就可以对上述代码做一下优化,查票可以一次性给所有人看,但是买票的时候必须一个一个来。
# 只需要这两处优化即可,就可以实现模拟抢票了
from multiprocessing import Process,Lock
def run(name,mutex):
    search(name)
    # 把买掉环节变成串行
    mutex.acquire()  # 抢锁
    buy(name) 
    mutex.release()  # 放锁

# 模拟多人同时抢票
if __name__ == '__main__':
    mutex = Lock()  # 互斥锁在主进程中产生一把,交给多个子进程使用
    for i in range(1,10):
        p = Process(target=run,args=(i,mutex))
        p.start()

这里是IT小白陆禄绯,欢迎各位大佬的指点!!!

posted @ 2022-04-19 21:05  陆禄绯  阅读(51)  评论(0)    收藏  举报