python基础入门之进程

python基础入门之进程

阻塞与非阻塞

程序在运行中表现的状态分为三种:阻塞、运行、就绪
阻塞:程序遇到IO阻塞。程序遇到IO阻塞会立马挂起,CPU马上切换,等到IO结束之后,再执行。
非阻塞:程序没有IO或者遇到IO通过某种手段让CPU去执行其他任务,尽可能的占用CPU。

同步与异步

从线程发布任务的角度来讲:
同步:任务发出去之后,等待,直到这个任务最终结束之后,给我一个返回值,在发布下一个任务。
def task():
    print('task is running')
    time.sleep(1)
    return f'task is finish'


if __name__ == '__main__':
    p = ProcessPoolExecutor(4)
    for i in range(8):
        obj = p.submit(task)
        print(obj.result())  
# 发出一个任务后,直到得到返回值才发布下一个任务
"""
task is running
task is finish
task is running
task is finish
......
"""

异步:所有任务同时发出,直接执行下一行
from concurrent.futures import ProcessPoolExecutor
import time


def task():
    print('task is running')
    time.sleep(1)
    return f'task is finish'


if __name__ == '__main__':
    p = ProcessPoolExecutor(4)
    obj_list = []
    for i in range(8):
        obj = p.submit(task)  # 发出任务后直接执行下一行
        obj_list.append(obj)
    time.sleep(4)  # 等待所有任务执行完毕
    p.shutdown(wait=True)
    # 1.阻止再向进程池投放新任务;
    # 2.wait=True十个任务是10,一个任务完成-1,直到为0,执行下一行,与join相似
    for i in obj_list:
        print(i.result()) 
# 异步回收任务的方式一:将所有任务结果统一回收
"""
输出结果:
先打印完毕所有的task is running,再打印task is finish
"""

以上看出:

同步发布任务:先把第一个任务交给进程,等到第一个进程完成后,再将第二个任务交给交给下一个进程。

异步发布任务:直接将10个任务发布给4个进程,不管结果如何,直接执行下一行代码。

综合使用

同步阻塞形式
     效率最低。
     进程调用函数后,进程需要等待函数的返回值,在等待的时候,该进程处于挂起状态,当函数返回之后,系统切换为内核态,将该进程改变为就绪态,进入工作队列,等待CPU调度。

异步阻塞形式
     效率低下,和同步类似。但进程只需等待函数返回收到,即可将进程转换为可运行状态。进程和函数约好使用回调或通知方式进行通信,等到处理完成,函数通过约好的方式将结果发送给进程。

同步非阻塞形式
     实际上是效率低下的。
     进程调用函数后,进程会继续运行。但由于函数在没有执行完成之前没有返回值,进程只能通过轮询进程和函数共享资源的方式来知道函数是否完成。

异步非阻塞形式
     效率更高。
     进程调用函数后,进程会继续运行。进程和函数约好使用回调或通知方式进行通信,等到处理完成,函数通过约好的方式将结果发送给进程。

创建进程的多种方式

"""
1.鼠标双击软件图标
2.python代码创建进程
"""
# from multiprocessing import Process
# import time
#
#
# def task(name):
#     print('task is running',name)
#     time.sleep(3)
#     print('task is over',name)


"""
在不同的操作系统中创建进程底层原理不一样
    windows
        以导入模块的形式创建进程
    linux/mac
        以拷贝代码的形式创建进程
"""
# if __name__ == '__main__':
#     # p1 = Process(target=task, args=('jason',))  # 位置参数
#     p1 = Process(target=task, kwargs={'name':'jason123'})  # 关键字参数
#     p1.start()  # 异步 告诉操作系统创建一个新的进程 并在该进程中执行task函数
#     # task()  # 同步
#     print('主')


from multiprocessing import Process
import time


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

    def run(self):
        print('run is running', self.name, self.age)
        time.sleep(3)
        print('run is over', self.name, self.age)


if __name__ == '__main__':
    obj = MyProcess('jason', 123)
    obj.start()
    print('主')

进程间数据隔离

# 同一台计算机上的多个进程数据是严格意义上的物理隔离(默认情况下)
from multiprocessing import Process
import time

money = 1000


def task():
    global money
    money = 666
    print('子进程的task函数查看money>>>:', money)


if __name__ == '__main__':
    p1 = Process(target=task)
    p1.start()
    time.sleep(3)
    print(money)
#输出结果:
子进程的task函数查看money>>>: 666
1000

进程的join方法

from multiprocessing import Process
import time


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


if __name__ == '__main__':
    p1 = Process(target=task, args=('jason1', 1))
    p2 = Process(target=task, args=('jason2', 2))
    p3 = Process(target=task, args=('jason3', 3))
    # p.start()  # 异步
    '''主进程代码等待子进程代码运行结束再执行'''
    # p.join()
    # print('主')
    start_time = time.time()
    p1.start()
    p1.join()
    p2.start()
    p2.join()
    p3.start()
    p3.join()
    # p1.join()
    # p2.join()
    # p3.join()
    print(time.time() - start_time)  # 3秒多

IPC机制

IPC:进程间通信
消息队列:存储数据的地方 所有人都可以存 也都可以取

from multiprocessing import Queue


q = Queue(3)  # 括号内可以指定存储数据的个数
# 往消息队列中存放数据
q.put(111)
# print(q.full())  # 判断队列是否已满
q.put(222)
q.put(333)
# print(q.full())  # 判断队列是否已满
# 从消息队列中取出数据
print(q.get())
print(q.get())
# print(q.empty())  # 判断队列是否为空
print(q.get())
# print(q.empty())  # 判断队列是否为空
# print(q.get())
print(q.get_nowait())

"""
full() empty() 在多进程中都不能使用!!!
"""


from multiprocessing import Process, Queue


def product(q):
    q.put('子进程p添加的数据')

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


if __name__ == '__main__':
    q = Queue()
    # 主进程往队列中添加数据
    # q.put('我是主进程添加的数据')
    p1 = Process(target=consumer, args=(q,))
    p2 = Process(target=product, args=(q,))
    p1.start()
    p2.start()
    print('主')

生产者消费模型

"""回想爬虫"""
生产者
	负责产生数据的'人'
消费者
	负责处理数据的'人'
    
该模型除了有生产者和消费者之外还必须有消息队列(只要是能够提供数据保存服务和提取服务的理论上都可以)

进程对象的多种方法

1.如何查看进程号
	from multiprocessing import Process, current_process
 	current_process()
 	current_process().pid  
	import os
 	os.getpid()
  	os.getppid()
2.终止进程
	p1.terminate()
	ps:计算机操作系统都有对应的命令可以直接杀死进程
3.判断进程是否存活
	p1.is_alive()
4.start()
5.join()

守护进程

守护进程会随着守护的进程结束而立刻结束
	eg: 吴勇是张红的守护进程 一旦张红嗝屁了 吴勇立刻嗝屁
      
from multiprocessing import Process
import time


def task(name):
    print('德邦总管:%s' % name)
    time.sleep(3)
    print('德邦总管:%s' % name)


if __name__ == '__main__':
    p1 = Process(target=task, args=('大张红',))
    p1.daemon = True
    p1.start()
    time.sleep(1)
    print('恕瑞玛皇帝:小吴勇嗝屁了')

僵尸进程与孤儿进程

僵尸进程
	进程执行完毕后并不会立刻销毁所有的数据 会有一些信息短暂保留下来
 	比如进程号、进程执行时间、进程消耗功率等给父进程查看
 	ps:所有的进程都会变成僵尸进程
孤儿进程
	子进程正常运行 父进程意外死亡 操作系统针对孤儿进程会派遣福利院管理

多进程数据错乱问题

模拟抢票软件

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 @ 2022-11-18 17:55  知了了了了  阅读(51)  评论(0)    收藏  举报