首页 > 系统相关 >同步异步、阻塞非阻塞、进程

同步异步、阻塞非阻塞、进程

时间:2022-11-18 22:46:05浏览次数:34  
标签:__ 异步 同步 name p1 阻塞 print task 进程

1.同步与异步

1.同步异步用来表达任务的提交方式

2.同步:同步是指一个进程在执行某个请求的时候,如果该请求需要一段时间才能返回信息,那么这个进程会一直等待下去,直到收到返回信息才继续执行下去。
'''代码执行到某行时,如果代码需要调用其他代码,那么就去其他文件执行代码,执行完之后再回来'''
    
3.异步:异步是指进程不需要一直等待下去,而是继续执行下面的操作,不管其他进程的状态,当有信息返回的时候会通知进程进行处理,这样就可以提高执行的效率了,即异步是我们发出的一个请求,该请求会在后台自动发出并获取数据,然后对数据进行处理,在此过程中,我们可以继续做其他操作,不管它怎么发出请求,不关心它怎么处理数据。

'''代码执行到某行时,如果代码需要调用其他代码,继续往下执行'''
"""
你和朋友去吃饭,你们到达饭店,点了一桌满汉全席。由于你们饿的不行,就在饭店等待厨师做好菜,等菜上桌。你和你的朋友吃完饭,付了钱,舒舒服服的去商场购物了,一次饭局就结束了。这就是同步调用。

如今,我们正处在互联网时代,当你们饿的时候,就打开饿了么,点了一桌满汉全席,支付了餐费,这时你就完成了点菜。商家接到了你的订单之后,就会马上开始安排(毕竟顾客就是上帝嘛)。现在你们无须默默等待,以免被饿所折磨,于是你们打开了京东,开始了新的购物。当饿了么小哥饭菜送到时,一签单就完事了,并且你们也完成了购物。这就是异步调用。
我们之前所写的代码都是同步调用。
"""

2.阻塞与非阻塞

阻塞与非阻塞表达任务的执行状态

1.阻塞:任务处于阻塞态
    
2.非阻塞:任务处于就绪态、运行态

3.综合使用

我们通常不会只看到上述单个词汇,而是会看到上述词汇的组合使用,含义举例说明:
1.同步阻塞:只运行这一个程序,提交任务之后,等待CPU回来继续运行,效率最低。
'''去银行排队,专心排队其他什么事情都不做'''
  
2.同步非阻塞:只运行这一个程序,提交完任务之后,可以原地做一些其他的事情。
'''还是用普通的汤锅,米放进去,然后去打游戏,过一会就来看一次 '''

3.异步阻塞:程序处于阻塞态,但是可以做一些其他操作。
'''去某宝淘了个电饭锅,饭熟了会自动跳闸的那种。米放进去,然后傻傻的看它怎么做饭的'''

4.异步非阻塞:提交完任务之后CPU马上去运行另一个程序,并且处于运行态或者就绪态,异步非阻塞效率最高。
'''米放进去,按下按钮,然后就去打游戏了,等到跳闸的时候就去吃饭。'''

4.创建进程的多种方式

1.两种方式:
	1.1 鼠标双击软件图标
	1.2 python代码
    
2.用python代码创建进程(方法一):
"""
不同的操作系统盘创建进程的底层原理不一样
windows:以导入模块的形式创建进程
linux:以拷贝代码的形式创建进程
"""
windows如果用代码创建进程,进程文件相当于导模块,会将原来的代码原封不动地拷贝一份到新进程,新进程执行到创建进程那一步会继续创建新进程,往复循环,直到报错。而苹果电脑创建的新进程会不包含创建新进程的那一步,所以苹果电脑创建新进程不会循环报错。windows代码及报错信息如下:
from multiprocessing import Process
import time

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

p1 = Process(target=task)
p1.start()
print('主进程')
"""
windows创建新进程是以导模块的形式创建,所以导模块的几步应该要加上验证是否是执行文件的:if __name__ == '__main__'。
"""
from multiprocessing import Process
import time

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

if __name__ == '__main__':
    p1 = Process(target=task,args=(111,))  # 创建一个子进程对象
    p1.start()  # 用子进程对象创建一个新的进程,并在该进程中执行task功能
    print('主进程')  # 执行结果:主进程 task is running 111 task is over 111
说明p1.start()生成一个新进程是一个异步操作,子进程中调用task函数,因为子进程拷贝代码需要时间,所以父进程中的print('主进程')会优先打印,打印结束之后会再执行子进程。

from multiprocessing import Process
import time

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

def func1():
    print('from func1')

if __name__ == '__main__':
    p1 = Process(target=task,args=(111,))
    p2 = Process(target=func1)
    p1.start()
    p2.start()
    print('主进程')
# 执行结果:主进程 task is running 111 from func1 task is over 111
一个父进程可以同时开多个子进程,子进程P1在读取的过程中先执行print('主进程'),再执行子进程p1中的print('task is running', name),此时子进程p1中还有部分代码未执行,由于要sleep3秒,此时先执行p2中的print('from func1'),最后执行print('task is over', name)。

3.用python代码创建进程(方法二):
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('max', 25)
    obj.start()
    print('主进程') 
#  主进程  run is running max 25  run is over max 25
该方法是利用类产生对象,然后用对象产生子进程的过程。还是先执行父进程中的print('主进程'),再执行子进程中的run is running max 25、run is over max 25。

5.进程的join方法、数据隔离

'''同一台计算机上的多个进程数据是严格意义上的物理隔离(默认情况下)'''
money = 1000

def task():
    money = 666

if __name__ == '__main__':
    task()
    print(money)
#  1000 print(money)找的是全局名称空间中的money

money = 1000

def task():
    global money
    money = 666

if __name__ == '__main__':
    task()
    print(money)
#  666 global局部修改全局,全局名称空间中的money被改成666

from multiprocessing import Process
import time

money = 1000

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

if __name__ == '__main__':
    p1 = Process(target=task)
    p1.start()
    time.sleep(3)
    print(money)
# 1000
"""
如果没有if __name__ == '__main__':及子代码,此时全局名称空间中的money已经被修改成666,局部名称空间中无money。但是要生成一个子进程,在子进程中调用task()函数,子进程中会调用全局名称空间中的money以及task()函数,task()函数执行修改的是子进程中全局名称空间中的money。task()函数体代码中查看的是子进程中的money,结果是666,说明已经修改。
"""

需求:如何实现子进程运行结束之后再运行主进程?
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=('max',))
    p.start()
    print('主进程')
# 主进程  max is running  max is over
"""
正常运行会先执行父进程的所有代码,再执行子代码,因为在子代码读取过程中CPU会先读取父代码并且执行
"""
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=('max',))
    p.start()
    time.sleep(4)
    print('主进程')
#  max is running  max is over 主进程 
"""
上述代码实现了先执行子进程再执行父进程,依靠的是父进程休眠时间总是比子进程长一些。但这种方法的弊端在于我们不知道子进程什么时候会休眠几秒,使用这种方式存在很多不确定性
"""
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=('max',))
    p.start()
    p.join()
    print('主进程')
#  max is running  max is over 主进程
"""
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=('max1',1))
    p2 = Process(target=task, args=('max2',2))
    p3 = Process(target=task, args=('max3', 3))
    start_time = time.time()
    p1.start()
    p2.start()
    p3.start()
    p1.join()
    p2.join()
    p3.join()
    print(time.time() - start_time)
"""
max1 is running
max2 is running
max3 is running
max1 is over
max2 is over
max3 is over
3.151333808898926
"""
"""
根据前六部打印顺序得知,第一个子进程先开始,不等睡眠结束就开始运行第二个,再第三个。start()连在一起说明无需等待上一个子进程结束就可以开始下一个。
"""
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=('max1',1))
    p2 = Process(target=task, args=('max2',2))
    p3 = Process(target=task, args=('max3', 3))
    start_time = time.time()
    p1.start()
    p1.join()
    p2.start()
    p2.join()
    p3.start()
    p3.join()
    print(time.time() - start_time)
"""
max1 is running
max1 is over
max2 is running
max2 is over
max3 is running
max3 is over
6.363624334335327
"""
"""
join()表示必须等子进程运行结束之后才能运行主进程,同时也表示上一个子进程结束之后才能运行下一个。
"""

6.IPC机制

IPC机制:同一台电脑上进程间通信
消息队列:存储数据的地方,所有人都可以存,也可以取,类似于一个仓库
"""
知识回顾:队列:先进先出;栈:先进后出
"""
from multiprocessing import Queue

q = Queue(3)
q.put(11)
print(q.full())  # False 判断队列是否已满
q.put(22)
q.put(33)
print(q.full())  # True
# q.put(44)  # 数量超过存放数量,不会报错,但是不会再执行下面的代码了,想要继续执行要把这一行注掉
print(q.get())  # 11
print(q.get())  # 22
print(q.empty())  # False 判断列表是否为空
print(q.get())
print(q.get_nowait())  # 拿完了,用get_nowait()继续拿会直接报错
"""
full(), empty()在多进程中不能使用,因为判断的上一刻列表数据可能就发生变化。聊天软件中的消息即使不上线也可以收到,因为消息存放在消息队列中。
"""

7.进程中通信

上述知识点我们得知了队列可以当做一个仓库来存放数据,那么队列是否可以当做不同进程之间数据联系的仓库呢?
from multiprocessing import Process, Queue

def consumer(q):
    '''在子进程中拿出数据'''
    print('子进程从队列当中拿到的数据:', q.get())

if __name__ == '__main__':
    q = Queue()
    '''在主进程中添加数据'''
    q.put('主进程添加的数据a')
    p1 = Process(target=consumer, args=(q,))
    p1.start()
    p1.join()
    print('主进程')
'''
子进程从队列当中拿到的数据: 主进程添加的数据a
主进程
'''
"""
上述代码我们得出结论:主进程和子进程可以依靠队列来进行数据交互
"""

from multiprocessing import Process, Queue

def product(q):
    q.put('子进程p2添加进队列的数据a')

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

if __name__ == '__main__':
    q = Queue()
    p1 = Process(target=consumer, args=(q,))
    p2 = Process(target=product, args=(q,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print('主进程')
'''
子进程p1从队列中拿到的数据: 子进程p2添加进队列的数据a
主进程
'''
"""
不仅子进程和主进程之间可以通过队列数据交互,子进程自检也可以进行数据交互
"""

8.生产者消费者模型(主要用在爬虫领域)

1.生产者:负责生产数据的'人';eg:爬虫爬取到的数据写入excel的过程。
        
2.消费者:负责处理数据的'人'。
"""
该模型除了上述两种类型,还应该有消息队列,生产者和消费者无直接关联,消息队列可以让生产者和消费者产生关联,并且可以对生产者产生的数据进行存储,也避免了两者需要同时工作的现象。消息队列也可以是数据库,文件,只要是能提供保存和提取服务的都可以
"""

9.进程对象的多种方法

1.在cmd窗口输入tasklist能看到系统给每一个进程分配的进程号,进程号和端口号一样都是为了方便管理
from multiprocessing import Process, current_process

def task():
   print(current_process())
   print(current_process().pid) # 获取子进程进程号

if __name__ == '__main__':
   print(current_process())  
   print(current_process().pid) # 获取主进程进程号
   p1 = Process(target=task)
   p1.start()
   p1.join()
   print('主进程')
'''
<_MainProcess(MainProcess, started)>
48692
<Process(Process-1, started)>
49216
主进程
'''

2.获取进程号也可以选用os模块中的os.getpid()方法,os.getppid()是查看父进程的进程号,其中子进程的父进程号和主进程号一致,因为主进程就是子进程的父进程。主进程的父进程是pycharm的进程号。
import os
from multiprocessing import Process

def task():
   print('子进程:', os.getpid())
   print('子进程的父进程', os.getppid())

if __name__ == '__main__':
   p1 = Process(target=task)
   p1.start()
   p1.join()
   print('主进程', os.getpid())
   print('主进程的父进程', os.getppid())

'''
子进程: 47784
子进程的父进程 46788
主进程 46788
主进程的父进程 2976
'''
3.杀死进程:在pycharm中可以用子进程对象.terminate()结束。
import os
from multiprocessing import Process

def task():
    print('子进程:', os.getpid())
    print('子进程的父进程', os.getppid())

if __name__ == '__main__':
    p1 = Process(target=task)
    p1.start()
    p1.terminate()
    print('主进程', os.getpid())
    print('主进程的父进程', os.getppid())

'''
子进程: 47784
子进程的父进程 46788
主进程 46788
主进程的父进程 2976
'''
"""
也可以在cmd窗口用taskkill /F /PID 进程号 来终止
"""
# 4
4.判断进程对象时候出存活:
def task():
    print('子进程:', os.getpid())

if __name__ == '__main__':
    p1 = Process(target=task)
    p1.start()
    print(p1.is_alive())  # True
    p1.terminate()
    print(p1.is_alive())  # True
"""
由于终止后程序还在运行,只需要让它睡一下,就会发现已经不再存活
"""
import os
from multiprocessing import Process
import time

def task():
    print('子进程:', os.getpid())

if __name__ == '__main__':
    p1 = Process(target=task)
    p1.start()
    print(p1.is_alive())  # True
    p1.terminate()
    time.sleep(1)
    print(p1.is_alive())  # False

10.守护进程

进程如果有守护进程,它关闭了那么它的守护进程也关闭
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=('进程1',))
    p1.daemon = True  # p1进程是主进程的守护进程
    p1.start()
    time.sleep(1)
    print('主进程')  # 主进程执行完毕,守护进程还没执行完,就已经结束
'''
进程1是子进程
主进程
'''

11.僵尸进程与孤儿进程

1.僵尸进程:进程执行完毕后并不会立刻销毁所有的数据,会有一些信息短暂保留下来,比如进程号、进程执行时间、进程消耗功率等给父进程查看
ps:所有的进程都会变成僵尸进程

2.孤儿进程:子进程正常运行,父进程意外死亡,操作系统针对孤儿进程会派遣福利院管理。

12.多进程数据错乱问题

模拟抢票软件

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()
   
"""
多进程操作数据很可能会造成数据错乱>>>:互斥锁
	互斥锁
		将并发变成串行 牺牲了效率但是保障了数据的安全
"""

标签:__,异步,同步,name,p1,阻塞,print,task,进程
From: https://www.cnblogs.com/zkz0206/p/16905083.html

相关文章