【并发编程】第2回 进程

1. 创建进程的多种方式

1.1 双击桌面程序图标

1.2 代码创建进程

  1. 创建进程的代码在不同的操作系统中,底层原理有区别,在windows中创建进程类似于导入模块,if name == 'main': 启动脚本。在mac,liunx中,创建进程类似于直接拷贝,不需要启动脚本,但是为了兼容性 也可以使用

1.2.1 方式1

  1. 代码编写
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=('jason',))  # 创建一个进程对象
    # p.start()  # 告诉操作系统创建一个进程(异步操作)
    task('jason')  # 普通的函数调用是同步操作
    print('主进程')
  1. 在task('jason') 情况下是普通的函数调用是同步操作先运行函数在运行主进程
  2. 在p.start()情况下,告诉操作系统创建一个进程(异步操作)先运行主进程在运行子进程

1.2.2 方式2

  1. 代码编写展示
class MyProcess(Process):
    def __init__(self,name):
        super().__init__()
        self.name = name

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

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

2. join方法

2.1 定义

  1. join:主进程等待子进程运行结束之后在运行

2.2 推导步骤1:直接在主进程代码中添加time.sleep()

  1. 不合理,因为无法准确把握子进程执行的时间

2.3 推导步骤2:join方法

2.3.1 运行顺序p1.start(),p1.join()....

import time
from multiprocessing import Process


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=('jason', 2))  # 可以通过kwargs={'name':'jason', 'n':1} 太麻烦 没必要
    p3 = Process(target=task, args=('jason', 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)
    print('主进程')

2.3.2 运行顺序p1.start()...,p1.join()...

import time
from multiprocessing import Process


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=('jason', 2))  # 可以通过kwargs={'name':'jason', 'n':1} 太麻烦 没必要
    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('总耗时:%s' % end_time)
    print('主进程')

3. 进程间数据默认隔离

3.1 本质

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

3.2 代码编写

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)

4. 进程间通信(IPC机制)

4.1 预备知识

4.1.1 什么是队列:先进先出

4.1.2 创建队列对象

# 1.创建队列对象
q = Queue(3)  # 括号内指定队列可以容纳的数据个数  默认:2147483647

4.1.3 往队列添加数据

# 2.往队列添加数据
q.put(11)
print(q.full())  # 判断队列是否已经存满
q.put(22)
q.put(33)
print(q.full())
q.put(44)  # 超出数据存放极限,那么程序一致处于阻塞态,直到队列中有数据被取出

4.1.4 从队列中取数据

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())  # 超出数据获取极限 那么程序一致处于阻塞态 直到队列中有数据被添加

4.1.5 总结

  1. q.full(),q.empty(),q.get_nowait()在多进程下不能准确使用(失效)

4.2 IPC机制

  1. 主进程与子进程通信
  2. 子进程与子进程通信

4.3 代码编写

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('主进程')

5. 生产者消费者模型

5.1 生产者:产生数据

5.2 消费者:处理数据

5.3 理解

  1. 如爬取红牛分公司
  2. 生产者:获取网页数据的代码(函数) 爬
  3. 消费者:从网页数据中筛选出符合条件的数据(函数) 筛选

5.4 完成的生产者消费者模型至少有三个部分

  1. 生产者
  2. 消息队列/数据库
  3. 消费者

6. 进程相关方法

6.1 查看进程号

# 1.查看进程号
from multiprocessing import current_process
import os
current_process().pid
os.getpid()
os.getppid()
print(os.getpid())  # 获取当前进程的进程号
print(os.getppid())  # 获取父进程的进程号

6.2 销毁子进程

p1.terminate()

6.3 判断进程是否存活

p1.is_alive()

6.4 守护进程

6.4.1 如何理解守护

  1. 伴随着守护对象的存活而存活,死亡而死亡

6.4.2 代码编写

  1. p.daemon = True 必须在start之前执行
from multiprocessing import Process
import time


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


if __name__ == '__main__':
    p = Process(target=task, args=('基佬',))
    # p.daemon = True  # 将子进程设置为守护进程:主进程代码结束 子进程立刻结束
    p.daemon = True  # 必须在start之前执行
    p.start()
    print('天子Jason寿终正寝')


2. p.daemon = True 在start之后执行

7. 僵尸进程与孤儿进程

7.1 僵尸进程

  1. 进程已经运行结束,但是相关的资料并吗,没用完全清空,需要父进程参与回收

7.2 孤儿进程

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

8. 互斥锁

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