并发编程二

创建编程的两种方式

"""
1.双击桌面程序图标
2.代码创建进程(需要掌握)
	需要掌握两种即可
"""
"""
创建进程的代码在不同的操作系统中 底层原理有区别!!!
在windows中 创建进程类似于导入模块
    if __name__ == '__main__':  启动脚本
在mac、linux中 创建进程类似于直接拷贝
    不需要启动脚本 但是为了兼容性 也可以使用
"""
from multiprocessing import Process
import time

#方式一
def task(name,n):  # 元组第一个传第一个位置一一对应
    print(f'{name}正在运行')
    time.sleep(2)
    print(f'{name}运行结束')


if __name__ == '__main__':
    p = Process(target=task, args=('jason',1))  # 创建进程对象  第一个  参数target= 进程名,第二参数可以传入数据args = (元组)
    p.start()  # 异步操作 运行之后会继续运行下面的代码可以看成是把代码拷贝一份在另一个文件
    # 运行除了启动脚本以外所有代码并调用函数当前代码中的函数没有运行
    p.join()
    print('老八秘制小汉堡')

#方式二
class MyProcess(Process):
    def __init__(self,name):

        super().__init__()
        self.name = name


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


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

join方法

join:主进程等待子进程运行结束之后再运行
    
推导步骤1:直接在主进程代码中添加time.sleep()
	不合理 因为无法准确把握子进程执行的时间
推导步骤2:join方法
	很合理!!!
在启动脚本中提交任务后加入进程对象.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)
    print('主进程')
'''一定要看准join的执行位置 以及多任务情况下等待的目标'''

进程间数据默认隔离

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

money = 100


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


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

进程间通信(IPC机制)

预备知识
	什么是队列:先进先出
		  from multiprocessing import Queue
        # 1.创建队列对象
        q = Queue(3)  # 括号内指定队列可以容纳的数据个数 默认:2147483647
        # 2.往队列添加数据
        # q.put(111)
        # print(q.full())  # 判断队列是否已经存满
        # q.put(222)
        # q.put(333)
        # print(q.full())
        # q.put(444)  # 超出数据存放极限 那么程序一致处于阻塞态 直到队列中有数据被取出
        # 3.从队列中取数据
        print(q.get_nowait())
        print(q.get_nowait())
        print(q.get_nowait())  # 队列中如果没有数据可取 直接报错
        # print(q.get())
        # print(q.empty())  # 判断队列是否已经空了
        # print(q.get())
        # print(q.get())
        # print(q.empty())
        # print(q.get())  # 超出数据获取极限 那么程序一致处于阻塞态 直到队列中有数据被添加
        """
            q.full()
            q.empty()
            q.get_nowait()
        上述方法在多进程下不能准确使用(失效)!!!
        """
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()  # 判断的时候需要给操作系统反应时间睡眠0.1秒

守护进程

如何理解守护
	伴随着守护对象的存活而存活 死亡而死亡
    
    
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('天子Jason寿终正寝!!!')
  • 父进程报错子进程继续运行,两个进程互不干涉,两个进程公用一个终端一个进程报错不影响其他进程使用终端

僵尸进程与孤儿进程

僵尸进程
	进程已经运行结束 但是相关的资源并没有完全清空
 	需要父进程参与回收
孤儿进程
	父进程意外死亡 子进程正常运行 该子进程就称之为孤儿进程
 	孤儿进程也不是没有人管 操作系统会自动分配福利院接收

互斥锁

模拟抢票
	查票
	买票
		查票
 		买票
 
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()

多个进程在不加锁的情况下容易发生数据错乱加锁即可

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

# 查票
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
        time.sleep(random.randint(1,3))
        with open(r'data.json', 'w', encoding='utf8') as f:
            json.dump(data, f)
        print('%s买票成功' % name)
    else:
        print('%s很倒霉 没有抢到票' % name)


def run(lock ,name):
    search(name)
    lock.acquire()
    buy(name)
    lock.release()



if __name__ == '__main__':
    lock = Lock()
    for i in range(500):
        p = Process(target=run, args=(lock,'用户%s' % i,))
        p.start()
posted @ 2022-08-09 17:46  懒羊羊A  阅读(24)  评论(0)    收藏  举报