【8.9】并发编程之进程介绍

今日内容概要

  • 创建进程的多种方式
  • join方法
  • 进程对象相关操作
  • 进程间数据默认隔离
  • 互斥锁
  • 生产者与消费者模型

创建进程的多种方式

第一种:
from multiprocessing import Process
import time


def task(name):
    print(f'{name}正常运行')
    time.sleep(5)
    print(f'{name}运行结束')

if __name__ == '__main__':
    p = Process(target=task,args=('jaosn',))  # 创建一个进程对象
    p.start()  # 告诉操作系统创建一个进程(异步操作)
    # task('jason')  # 普通的调用函数是同步操作
    print('主进程')  # 执行结果为 1.主进程 2.jaosn正常运行 3.jaosn运行结束
==============================================================
注意:创建进程的代码在不同的操作系统中  底层原理是有区别的!
	 在windows中 创建进程类似于导入模块
    if __name__ == '__main__':  启动脚本 
      在mac linux 中 创建进程类似于直接拷贝  不需要启动脚本 但是为了兼容性 也可以使用启动脚本
===============================================================    
    
第二种:类方法也可以创建进程
class MyProcess(Process):
    def __init__(self, name):
        super().__init__()
        self.name = name

    def run(self):
        print(f'{self.name}正在运行')
        time.sleep(5)
        print(f'{self.name}运行结束')


if __name__ == '__main__':
    obj = MyProcess('jason')
    obj.start()
    print('主进程')
    

join方法

  • join:主进程等待子进程运行结束之后再运行
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=('jason', 1))  # args就是通过元组的形式给函数传参
    p2 = Process(target=task, args=('kevin', 2))  # 也可以通过kwargs={'name':'jason', 'n':1} 比较麻烦
    p3 = Process(target=task, args=('jerry', 3))
    start_time = time.time()
    p1.start()
    p1.join()
    p2.start()
    p2.join()
    p3.start()
    p3.join()
    end_time = time.time() - start_time
    print('总耗时:%s' % end_time)  # 总耗时:6.406511545181274
    print('主进程')
    
# 注意:这样join方法 会形成阻塞  耗时会增加  操作同步    
    
    
    
    
def task(name, n):
    print(f'{name}正在运行')
    time.sleep(n)
    print(f'{name}运行结束')


if __name__ == '__main__':
    p1 = Process(target=task, args=('jason', 1))  # args就是通过元组的形式给函数传参
    p2 = Process(target=task, args=('kevin', 2))  # 也可以通过kwargs={'name':'jason', 'n':1} 太麻烦 没必要
    p3 = Process(target=task, args=('jerry', 3))
    start_time = time.time()
    p1.start()
    p2.start()
    p3.start()
    p1.join()
    p2.join()
    p3.join()
    end_time = time.time() - start_time
    print('总耗时:%s' % end_time)  # 总耗时:3.24733829498291
    print('主进程')
    
总结:join的位置变化了 形成异步操作

进程间数据默认隔离

  • 多个进程数据彼此之间默认是相互隔离的
    • 如果真的想交互 需要借助于 '管道' 或者 '队列'
from multiprocessing import Process

money = 100


def task():
    global money
    money = 666
    print('子进程打印的money', money)  # 子进程打印的money 666


if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    p.join()
    print('父进程打印的money', money)  # 父进程打印的money 100

进程间通信(IPC机制)

IPC机制
	1.主进程与子进程通信
 	2.子进程与子进程通信
 
from multiprocessing import Queue, Process


def procedure(q):
    q.put('子进程procedure往队里中添加了数据')


def consumer(q):
    print('子进程的consumer从队列中获取数据', q.get())


if __name__ == '__main__':
    q = Queue()  # 在主进程中产生q对象 确保所有的子进程使用的是相同的q
    p1 = Process(target=procedure, args=(q,))
    p2 = Process(target=consumer, args=(q,))
    p1.start()
    p2.start()
    print('主进程')

生产者消费者模型

  • 生产者:生产数据
  • 消费者:处理处理
  • 举例:例如爬取网页信息
    • 生产者:获取网页数据的代码(函数)
    • 消费者:从网页数据中筛选出符合条件的数据(函数)
  • 完整的生产者消费者模型至少有三个部分
    • 生产者
    • 消息队列/数据库
    • 消费者

进程相关方法

1.查看进程号
	from multiprocessing import current_process
	import os
	current_process().pid
	os.getpid()
	os.getppid()
2.销毁子进程
	p1.terminate()
3.判断进程是否存活
	p1.is_alive()

守护进程

  • 守护进程的意思为:
    • 伴随着守护对象的存活而存活 死亡而死亡
from multiprocessing import Process
import time


def task(name):
    print('坤坤:%s存活' % name)
    time.sleep(3)
    print('坤坤:%s嗝屁' % name)


if __name__ == '__main__':
    p = Process(target=task, args=('基佬',))
    p.daemon = True  # 将子进程设置为守护进程:主进程代码结束 子进程立刻结束
    p.start()
    # p.daemon = True  # 必须在start之前执行
    print('坤坤在悄然离逝!')  
    
# 结果为:坤坤在悄然离逝
# 因为:主进程代码结束 子进程立刻结束

僵尸进程与孤儿进程

  • 僵尸进程 :

    • 进程已经运行结束 但是相关的资源并没有完全清空
    • 需要父进程参与回收
  • 孤儿进程:

    • 父进程意外死亡 子进程正常运行 该子进程就称之为孤儿进程
    • 孤儿进程也不是没有人管 操作系统会自动分配福利院接收

互斥锁

  • 含义:
    • 互斥锁的意思就是互相排斥,如果把多个进程比喻为多个人,互斥锁的工作原理就是多个人都要去争抢同一个资源:卫生间,一个人抢到卫生间后上一把锁,其他人都要等着,等到这个完成任务后释放锁,其他人才有可能有一个抢到…所以互斥锁的原理,就是把并发改成穿行,降低了效率,但保证了数据安全不错乱
# 模拟抢票  查票  抢票
# 数据库(data.json)  数据为:{"ticket_num": 1}

from multiprocessing import Process
import time
import json
import random


# 查票
def search(name):
    with open(r'data.json', 'r', encoding='utf8') as f:
        data = json.load(f)
    print('%s在查票 当前余票为:%s' % (name, data.get('ticket_num')))


# 买票
def buy(name):
    # 再次确认票
    with open(r'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'data.json', 'w', encoding='utf8') as f:
            json.dump(data, f)
        print('%s买票成功' % name)
    else:
        print('%s很倒霉 没有抢到票' % name)


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


if __name__ == '__main__':
    for i in range(10):
        p = Process(target=run, args=('用户%s'%i, ))
        p.start()
        
# 查看车票时  都可以看到剩余一张车票  抢票时 全部乘车都抢购到了车票  出现了错乱
# 并发运行,效率高,但竞争写同一文件,数据写入错乱,只有一张票,卖成功给了10个人
  • 过程:查看车票时 都可以看到剩余一张车票 抢票时 全部乘车都抢购到了车票 出现了错乱

  • 结果:并发运行,效率高,但竞争写同一文件,数据写入错乱,只有一张票,卖成功给了10个人

  • 解决方式:加锁处理~~购票行为由并发变成了串行,牺牲了运行效率,但保证了数据安全

posted @   W日常  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示