逆袭之路——python 网络编程之进程的创建及属性方法、互斥锁【day35】

逆袭之路——python 网络编程之进程的创建及属性方法、互斥锁【day35】,第1张

今日内容概要
  • 代码创建进程
  • multiprocess模块与multiprocess.process模块
  • join方法
  • 进程间数据默认隔离
  • 进程对象属性和方法
  • 僵尸进程与孤儿进程
  • 守护进程
  • 互斥锁(重要)
今日内容详细 一、代码创建进程

        对于通用系统(跑很多应用程序),需要有系统运行过程中创建或撤销进程的能力,主要分为4中形式创建新的进程:

  1. 系统初始化(查看进程linux中用ps命令,windows中用任务管理器,前台进程负责与用户交互,后台运行的进程与用户无关,运行在后台并且只在需要时才唤醒的进程,称为守护进程,如电子邮件、web页面、新闻、打印)

  2. 一个进程在运行过程中开启了子进程(如nginx开启多进程,os.fork,subprocess.Popen等)

  3. 用户的交互式请求,而创建一个新进程(如用户双击暴风影音)

  4. 一个批处理作业的初始化(只在大型机的批处理系统中应用)

        无论哪一种,新进程的创建都是由一个已经存在的进程执行了一个用于创建进程的系统调用而创建的。

"""
"""
创建进程的方式有哪些
	1.鼠标双击桌面一个应用图标
	2.代码创建
	
创建进程的本质:在内存中申请一块内存空间用于运行相应的程序代码
"""
# 第一种创建进程的方式
 from multiprocessing import Process
 import time


 def task(name):
     print('%s is running' % name)
     time.sleep(3)
     print('%s is over' % name)


 if __name__ == '__main__':
     p = Process(target=task, args=('jason',))  # 创建一个进程对象
     p.start()  # 告诉 *** 作系统创建一个新的进程
     print('主进程')
"""
强调:不同的 *** 作系统创建进程的要求不一样
    在windows中创建进程是以导入模块的方式进行 所以创建进程的代码必须写在__main__子代码中
    否则会直接报错 因为在无限制创建进程
    在linux和mac中创建进程是直接拷贝一份源代码然后执行 不需要写在__main__子代码中
"""
# 创建进程的第二种方式
from multiprocessing import Process
import time
class MyProcess(Process):
    def __init__(self, username):
        self.username = username
        super().__init__()
    def run(self):
        print('你好啊 小姐姐',self.username)
        time.sleep(3)
        print('get out!!!',self.username)
if __name__ == '__main__':
    p = MyProcess('tony')
    p.start()
    print('主进程')
二、multiprocess模块

        multiprocess不是一个模块而是python中一个 *** 作、管理进程的包,multi是多功能的意思,也就是在这个包中几乎包含了和进程有关的所有子模块。

multiprocess.process模块 process模块介绍
  • process模块是一个创建进程的模块,借助这个模块,就可以完成进程的创建

注意

  1. 需要使用关键字的方式来指定参数
  2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号

参数介绍

  1. group参数未使用,值始终为None
  2. target表示调用对象,即子进程要执行的任务
  3. args表示调用对象的位置参数元组,args=(1,2,‘jason’,)
  4. kwargs表示调用对象的字典,kwargs={‘name’:‘jason’,‘age’:18}
  5. name为子进程的名称

方法介绍

  1. p.start():启动进程,并调用该子进程中的p.run()
  2. p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
  3. p.terminate():强制终止进程p,不会进行任何清理 *** 作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁
  4. p.is_alive():如果p仍然运行,返回True
  5. p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程

在 windows中使用process模块的注意事项

        在Windows *** 作系统中由于没有fork(linux *** 作系统中创建进程的机制),在创建子进程的时候会自动 import 启动它的这个文件,而在 import 的时候又执行了整个文件。因此如果将process()直接写在文件中就会无限递归创建子进程报错。所以必须把创建子进程的部分使用if __name__ ==‘__main__’ 判断保护起来,import 的时候 ,就不会递归运行了。

三、join 方法
  • 定义:让主进程代码等待子进程代码运行完毕再执行
  • 多个进程同时运行(注意,子进程的执行顺序不是根据启动顺序决定的)
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=('jason', 1))
    p2 = Process(target=task, args=('tony', 2))
    p3 = Process(target=task, args=('kevin', 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}')  # 主进程 总耗时:3.015652894973755
    # 如果是一个start一个join交替执行 那么总耗时就是各个任务耗时总和
    
"""
需求:想让p.start()之后的代码 等待子进程全部运行结束之后再打印
    1.sb(sweet baby)做法:直接sleep
        肯定不行 因为子进程运行的时间不可控
    2.join方法
        针对多个子进程的等待一定要理解!!!
"""
四、 进程之间的内存空间是隔离的
  • 内存可以看成是有很多个小隔间组成的 彼此不干扰
  • 是使用局部修改全局不可变类型
  • 在windows系统中应该把全局变量定义在if __name__ == '__main__'之上就可以了
from multiprocessing import Process

money = 999

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

if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    p.join()  # 确保子进程代码运行结束再打印money
    print(money)
 
"""默认隔离  但是可以通过一些技术打破"""
五、 进程对象属性和方法
1.查看进程号的方法
	1.1.current_process函数
  		from multiprocessing import Process, current_process
    	current_process().pid
 		# 获取进程号的用处之一就是可以通过代码的方式管理进程
  		windows  			taskkill关键字
    	mac/linux  		kill关键字
    1.2.os模块
  		os.getpid()  # 获取当前进程的进程号
    	os.getppid()  # 获取当前进程的父进程号
2.杀死子进程
	terminate()
3.判断子进程是否存活
	is_alive()
六、 僵尸进程与孤儿进程
  • 僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程。
  • 孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所‘收养’,并由init进程对它们完成状态收集工作。
七、 守护进程
  • 会随着主进程的结束而结束。

  • 主进程创建守护进程
      其一:守护进程会在主进程代码执行结束后就终止
      其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children

注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止

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=('赵公公',))
    # 必须写在start前面
    p.daemon = True  # 将子进程设置为守护进程:主进程结束 子进程立刻结束
    p.start()
    print('皇帝Jason寿终正寝')
八、 互斥锁(重要)
  • 互斥锁: 对共享数据进行锁定,保证同一时刻只能有一个线程去 *** 作。
  • 注意: 互斥锁是多个线程一起去抢,抢到锁的线程先执行,没有抢到锁的线程需要等待,等互斥锁使用完释放后,其它等待的线程再去抢这个锁

老师通过类比人们抢票来形容互斥锁:

# 代码模拟抢票(有问题)
import json
from multiprocessing import Process
import time
import random


# 查票
def search(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):
    '''
    点击买票是需要再次查票的 因为期间其他人可能已经把票买走了
    '''
    # 1.查票
    with open(r'ticket_data.json', 'r', encoding='utf8') as f:
        data = json.load(f)
    time.sleep(random.randint(1, 3))
    # 2.判断是否还有余票
    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):
    search(name)
    buy(name)


# 模拟多人同时抢票
if __name__ == '__main__':
    for i in range(1, 10):
        p = Process(target=run, args=('用户:%s' % i,))
        p.start()

注意:
        当多个进程 *** 作同一份数据的时候会造成数据的错乱!!!
        这个时候需要加锁处理(互斥锁)
                将并发变成串行 牺牲了效率但是保证的数据的安全

        互斥锁并不能轻易使用 容易造成死锁现象
        互斥锁只在处理数据的部分加锁 不能什么地方都加 严重影响程序的效率

        查票可以一次性给所有人看 但是买票环节必须’排队’>>>:互斥锁

from multiprocessing import Process, Lock
mutex = Lock()
mutex.acquire()  # 抢锁
mutex.release()  # 放锁

ps:我们以后在编程生涯中几乎不会自己 *** 作锁 理解原理即可

"""
锁相关知识
	行锁:针对行数据加锁 同一时间只能一个人 *** 作
	表锁:针对表数据加锁 同一时间只能一个人 *** 作
锁的应用范围很广 但是核心都是为了保证数据的安全!!!
"""

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/714250.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-24
下一篇 2022-04-24

发表评论

登录后才能评论

评论列表(0条)

保存