pthon三十七期---同步异步,阻塞非阻塞,创建进程的方法,进程间数据隔离,join方法,进程通信的ipc机制,进程对象的诸多方法,互斥锁

昨日内容回顾

黏包现象

TCP
	可靠协议、流式协议
	特点:
	1. 可以讲数据量比较小的,发送时间间隔较短的数据统一一次性打包发过去。
	2. recv在接收的时候,不知道即将要接收的数据有多大。

.
.

struct模块

将非固定长度的数字打包成固定长度 并且可以反向解析出打包前数字!!!

import struct
struct.pack()
struct.unpack()

.
.

黏包问题的解决方案

客户端给服务端发消息
客户端
	1.制作一个真实数据相关的字典
	2.将字典序列化并编码统计长度
	3.利用struct模块对上述长度做打包处理
	4.直接发送打包之后的数据
	5.再发送字典数据
	6.最后发送真实数据
--------------------------
服务端
	1.接收固定长度的报头
	2.利用struct模块反向解析出字典数据的长度
	3.接收字典数据并处理成字典
	4.根据字典中的信息接收真实数据

.

  • 操作系统发展史
1.穿孔卡片
2.联机批处理系统
3.脱机批处理系统
ps:CPU利用率的发展史

.

  • 多道技术
单道技术
	排队执行
多道技术
	并发效果

切换+保存状态
	切换
     	 1.程序有IO操作
     	2.程序长时间占用
	保存状态

.

  • 进程理论
进程:正在运行的程序  进程肯定是在内存里面的!!!
进程调度算法
	1.先来先服务
	2.短作业优先
	3.时间片轮转法+多级反馈队列

.
.

  • 并行与并发
并行
	多个进程或任务同时执行,只能由多个cpu来实现
并发
	看上去像同时执行
高并发:评估程序同时服务客户端数量的能力
----------------------------------------
并行也可以算并发,并发不能算并行!!!

.

  • 进程的三状态
就绪态
运行态
阻塞态

今日内容概要

  • 同步与异步
  • 阻塞与非阻塞
  • 创建进程的多种方式
  • 进程join方法
  • 进程间数据隔离
  • 进程间通信之IPC机制
  • 进程对象诸多方法
  • 生产者消费者模型
  • 互斥锁

今日内容详细

同步与异步 重要!!!!!!

用来表达任务的提交方式

同步:提交完任务之后原地等待任务的返回结果,期间不做任何事
----------------------------
异步:提交完任务之后不愿地等待任务的返回结果,直接去做其他事,有结果自动通知
--------
比如代码运行到某一行,如果这一行代码需要调用其他的代码。
同步就是:跑到要调用的代码处执行完后,再回来继续往下。
异步就是:运行到调用其他代码的时候,立马往下执行,不会等待要调用的代码走完才往下执行。
-----------------------------------
所谓同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,该任务才能算完成,这是一种可靠的任务序列
-------------
所谓异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了
-------------
去银行办业务
第一种:排队等候:就是同步等待消息通知,也就是我要一直在等待银行办理业务情况;

第二种:取票等待:等待银行人叫号,就是异步等待消息通知。在异步消息处理中,等待办理业务的人往往有一个回调机制,在所等待的事件被触发时由触发机制(在这里是柜台的人)通过某种机制(在这里是写在小纸条上的号码,喊号)找到等待该事件的人。

.
.
.

阻塞与非阻塞

用来表达任务的执行状态
阻塞
	阻塞态
非阻塞
	就绪态、运行态

.
.
.
.
.

综合使用

同步阻塞:  提交完一个任务后原地等待什么事也做不了,cpu也走了,且程序处于一个阻塞态!!!
比如:在银行排队半业务,就站那等,其他什么事也不做!!

-----------------------

同步非阻塞: 比如,提交完一个任务后原地等待,但是可以在原地做一些事情,比如打电话等。没有被阻塞在等待的操作上,但是还只能站在那排队

-----------------------

异步阻塞:  如果在银行等待办理业务的人采用的是异步的方式去等待消息被触发(通知),
也就是领了一张小纸条,假如在这段时间里他不能离开银行做其它的事情,
那么很显然,这个人被阻塞在了这个等待的操作上面;

------------------------

异步非阻塞(******)  效率最高!!!没有任何的停顿,立刻就可以做其他事
提交完一个任务后,不等待,直接去做其他事,并且整个程序不处于非阻塞态!!!
比如你突然发觉自己烟瘾犯了,需要出去抽根烟,于是他告诉大堂经理说,
排到我这个号码的时候麻烦到外面通知我一下,那么他就没有被阻塞在这个等待的操作上面,
自然这个就是异步+非阻塞的方式了。

-------------------------
不论是排队还是使用号码等待通知,如果在这个等待的过程中,
等待者除了等待消息通知之外不能做其它的事情,那么该机制就是阻塞的,
表现在程序中,也就是该程序一直阻塞在该函数调用处不能继续往下执行。

.
.
.

创建进程的多种方式

1.鼠标双击软件图标
2.python代码创建进程
----------------------------------------------------

在不同的操作系统中创建新的进程底层原理不一样!!!!!!
windows
	以导入模块的形式创建进程!!!

----------------------------------------------------

当加了一行创建新的进程的代码后,会以导入模块的形式,
从上往下再执行一次该py文件里面的代码!!!
然后把执行之后的代码产生的结果,也放到一个新的内存空间里面去!!!
python在导模块的时候,只要程序不结束,已经导过的模块就不用再导了!!
所以就直接从导入模块的语句下面开始执行代码了!!
如果创建新的进程的代码,在普通代码的下面,就会出现代码一直反复执行循环的问题,
导致windows频繁的创建进程!!!就会自动报错了!!!

-----------------------------------------------------
如何解决?利用下列语句:
if __name__ == '__main__':
-----------------------------------------------------

在该语句下面的代码,只有是在执行文件的时候才会执行!!
所以把创建新的进程的代码发在该语句下面,就可以避免反复执行的问题了!!!
因为第一次创建新的进程的代码可以正常执行,但是windows是以导入模块的形式创建进程的,
所以第二次重上往下执行代码的时候,是以模块的形式执行代码的,
所以走到if __name__ == '__main__':  时就走不了了!!!

----------------------------------------------------

linux/mac
以拷贝代码的形式,创建新的进程的代码,上面的所有代码复制一份,创建进程的代码不复制!!
	然后创建一个新的进程,所以不会像windows一样报错!!!!!!

----------------------------------------------------

.
.
.

创建进程的代码-------------multiprocessing 模块重要!!!

想让某个函数反复的执行就可以用到创建进程的代码!!!
可以实现让py文件里面的某些代码实现在不同的内存空间里面执行!!!
.
.
.

创建新进程的第一种方式 重点学第一个,简单点!!!

先把要在子进程里面运行的函数task定义好,然后用process(target=task)类加括号产生一个对象,然后对象点start()才能创建一个子进程了!!!

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=('jason',))  # 按位置参数,给子进程传参!!
     p1.start()  # 异步操作!!告诉操作系统创建一个新的进程,并在该进程种执行task函数
     print('主')

# p1 = Process(target=task, kwargs={'name':'jason123'})
# 按关键字参数,给子进程传参!!  不推荐用,不方便!!

--------------------------
Process是个类加括号生成一个对象,Process有一个父类,父类里面有一个start方法,
作用就是创建一个子进程!!

要给子进程传参数,需要在target=XXX 关键字参数后面再加对应的args=('',)
或者kwargs={'':''} 往里面传对应的参数!!!
当只有一个参数时args=('',)括号里面的逗号不能省!!!
------------------------------------------------

如果直接调用task函数就变成同步操作了,等函数运行完才能往下走!!
image
.
p1.start() 这行代码是异步操作!!!告诉操作系统要开一个进程,代码不再等,直接往下走!!!
注意主进程里面并没有调用task函数!!!
image
.
假如在if name == 'main':下面多写几个创建子进程的代码,因为是异步操作,
所以几乎同一时间,内存空间会多出几个地方用来运行子进程,并在子进程中执行task函数。
现在就可以实现一个函数在同一时间执行多次的效果了!!!
image
image
.
.
.
.

创建新进程的第二种方式

创建一个类继承process,然后把需要在子进程中运行的函数代码放到该类中,
但是该函数名一定要起成run,最后类名加括号产生对象,对象点start()创建子进程!!!

from multiprocessing import Process
import time

class MyProcess(Process):  # 创建一个类,一定要继承Process这个类
	def run(self):
		print('run is running')
		time.sleep(3)
		print('run is over')

if __name__ == '__main__':
    obj = MyProcess()  # 我们自己定义的类产生一个对象
    obj.start()  # 对象去点父父类里面的start方法!!创建子进程!!
    print('主')

这样就实现了创建新的进程了
-------------------------------------------------
下面,怎么给子进程传参数:
class MyProcess(Process):   # 创建一个类,一定要继承Process这个类
    def __init__(self, name, age):   # 重写了MyProcess的__init__方法
        super().__init__()  # 重新调用父类Process里面的__init__方法,这样MyProcess产生的对象就拥有了父类Process里面所拥有有的一切属性了!!!
        self.name = name
        self.age = age
----
    def run(self):
        print('run is running', self.name)
        time.sleep(3)
        print('run is over', self.age)

# Process 类里面内置有一个name的属性,不重置name对应的就是一个MyProcess-1
----
if __name__ == '__main__':
    obj = MyProcess('jason', 123)
    obj.start()
    print('主')

.
.
.
.

进程间数据隔离

同一台计算机上的多个进程数据是严格意义上的物理隔离(默认情况下不能交互的)!!!

注意一个进程就是一个内存空间!!

比如在一台电脑上开两个qq,两个qq账号不一样,那么两个qq里面的聊天消息一定不会混掉或串掉

from multiprocessing import Process
import time

money = 1000

def task():
    money = 666

if __name__ == '__main__':
	task()
	print(money)  # 主进程代码打印money 结果是1000

.
.

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)  # 主进程代码等待3秒,等待3秒的过程中,子进程代码肯定运行完了!!
    print(money)      # 主进程代码打印money  结果还是1000!!!

当右键运行py文件的时候,立刻会创建该py文件的主进程,创建一个名称空间!!!
该名称空间里面先定义 money=1000  再定义一个task函数但是没有调用!!!

当走到p1.start()时,会创建一个新的进程,又会创建一个新的名称空间!!
以导模块的形式,代码又从上往下走一遍!!在新的内存空间里面又会定义一个  money=1000
再定义一个task函数
并且运行task函数,此时task函数里面改的是子进程里面的全局的money!!!
和主进程里面的全局money没关系!!!

image
.
image
.
.
.
.

进程的join方法 .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))
    p1.start()      # 异步操作
    p1.join()       # 主进程代码等待子进程代码运行结束再执行!!!!!!
    print('主')
-------------------------------------------------
复杂一点:
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))
    start_time = time.time()
    p1.start()  # 创建子进程1
    p2.start()  # 创建子进程2
    p3.start()  # 创建子进程3,异步操作,所以这3个进程,几乎同一时间创建
    p1.join()  # 等着第一个子进程结束,主进程才能往下走!!!!!!
    p2.join()
    p3.join()
    print(time.time() - start_time)    # 3秒多

3个子进程几乎同时创建,开始分别运行task函数,
第一个子进程睡一秒,第二个子进程睡两秒,第三个进程睡3秒,
所以当所以当第一个进程运行结束,睡完一秒的时候。其他两个进程也睡了一秒了。
当第二个进程运行结束睡完两秒的时候,第三个进程也睡了两秒了,
说以最后3个经常都走完的时间就只有3秒多一点!!!

--------------------------------
复杂一点:
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))
    start_time = time.time()
    p1.start()  # 创建子进程1
    p1.join()  # 等着第一个子进程结束,主进程才能往下走!!!!!!
    p2.start()  # 创建子进程2
    p2.join()
    p3.start()  # 创建子进程3,异步操作,所以这3个进程,几乎同一时间创建
    p3.join()
    print(time.time() - start_time)    # 6秒多点
-------------
第一个子进程创建后,因为下面有join方法,
所以必须在第一个子进程运行结束后,主进程代码才能往下走,
所以必须是第一个子进程运行结束了,才能走到创建子进程2的代码!!!

image
.
image
.
.
.
.

IPC(进程间通信)


消息队列最流弊的地方就是:
打破同一台计算机上多个进程之间数据的物理隔离,基于队列可以实现数据交互!!!!

消息队列Queue:  公共存储数据的地方,所有人都可以存,也都可以取!!!
类似于公共的仓库!!!队列先进先出!!!

from multiprocessing import Queue

q = Queue(3)  # 先产生一个队列的对象,括号内可以指定存储数据的个数,就是给对象添加独有属性!!!当括号里面不传值的时候,默认可以容纳很多很多个数据个数!!!
q.put(111)  # 往消息队列中存放数据
q.put(222)
q.put(333)
print(q.full())     # 判断队列是否已满!!      True
print(q.get())  # 从消息队列中取出数据!!    111    先进先出原则
print(q.get())  # 222
print(q.get())  # 333
print(q.empty())  # 判断队列是否为空!!      True
print(q.get_nowait())  # 拿数据,如果队列里面没数据就报错,不会像get等待!!

-------------------------------------------

注意如果Queue(3),那么生成的队列对象就只能放3个数据,
如果q.put()已经放了3次数据了,再q.put()代码就卡住了(进入IO状态),也不报错,
一直等到有其他进程把这个队列里面的数据取出一个后,才能继续运行,
同理q.get()也是的,当消息队列里面已经没数据,再运行q.get()时,也会卡住,也不报错,
等其他进程往队列里面放数据后,才能继续运行!!!

--------------------------------------------

.
.

注意:full() empty() 在多进程中都不能使用!!!


一个判断队列是不是满的,一个判断队列是不是为空,在多进程中都不能使用!!!
判断会出现失误!!!
当多进程的时候,多个进程都可以往队列里面放数据和取数据。
这个时候就出现一种极限情况了:当队列已经满的情况下,
当一个进程判断队列是否已满刚判断完,另一个进程立马从队列里面取了一个数据,
这个时候判断的结果就失真了!!!
同理一个进程判断完队列为空后,立马又有一个进程往队列里面塞了一个数据,
这个时候判断的结果就失真了!!!

image
.
.
.

IPC机制进程间通信------代码实操

进程间通信有两种情况:

1. 主进程与子进程交流!!!


主进程与子进程交流!!!
from multiprocessing import Process, Queue

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

if __name__ == '__main__':
    q = Queue()
    q.put('我是主进程添加的数据')            # 主进程往队列中添加数据
    p1 = Process(target=consumer, args=(q,))
	# 创建进程对象,要把队列对象q给传给consumer函数,这样consumer函数体代码就可以用到q了
    p1.start()
    p1.join()
    print('主')

子进程中的函数consumer函数体代码可以拿到主进程中的队列对象q,
从而在子进程中拿到了主进程放到队列里面的数据!!!

-------------------------------------------------------

image
.
.
.

2. 两个子进程间交流!!!

两个子进程间交流!!!     主进程不再添加数据了!!!
from multiprocessing import Process, Queue

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

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

if __name__ == '__main__':
    q = Queue()
    p1 = Process(target=consumer, args=(q,))
    p2 = Process(target=product, args=(q,))
    p1.start()
    p2.start()
    print('主')

两个子进程间也是可以通过队列来实现,数据交互的!!!
-------------------------------------

image
.
.

为什么要开多进程的?


比如:提前定义一些函数,因为一个函数就是一个具体的功能,假设定义了十个函数,
十个函数分别要干不同的事情,那我们学了多进程后,就可以同时开十个进程,
同时让十个进程干不同的事,
这样的效率就很高了!!!

.
.
.
.

生产者消费者模型


"""主要用在爬虫领域!!!"""
生产者(爬取数据的代码)
	负责产生数据的'人'
消费者(正则筛选数据的代码)
	负责处理数据的'人'

-------------------------------------

该模型除了有生产者和消费者之外
还必须有消息队列(只要是能够提供数据保存服务和提取服务的理论上都可以)!!!

一般情况下:生产出来的数据,消费者要消费的数据,生产者与消费者之间不是直接关联的!!!
这样生产者与消费者都不用等待彼此了!!!!!!
有了消息队列的存在就可以实现生产者与消费者之间的解耦合,也就是可以不同时出现!!!

.
.
.
.

python中进程对象的多种方法(了解)


1.如何查看进程号(pid就是process id 进程号的意思)
	from multiprocessing import Process, current_process
	current_process()      # 类加括号生成一个当前进程对象!!
	current_process().pid           # 获取当前进程的进程号
---------------------------------------------------
	import os
	os.getpid()   # 也可以获取当前进程的进程号
	os.getppid()      # 获取当前进程的主进程号
-----------------------------------------------------------
2.终止进程
	p1.terminate()      # 终止子进程p1  也是异步操作

-----------------------------------------------------------
windows的cmd命令也可以终止进程
先用tasklist 看一下你要终止的进程的进程号

taskkill /pid XXX  终止进程号是XXX的进程!!!

taskkill /F /pid XXX  强制终止进程号是XXX的进程!!!
-----------------------------------------------------------
3.判断进程对象是否存活
	进程对象.is_alive()
-----------------------------------------------------------
4.  进程对象.start()
5.  进程对象.join()      主进程代码等待子进程代码执行 完成后再执行!

强制终止进程号是XXX的进程!!!
image
.
.
.
.
.
.

守护进程-------重要 项目启动的时候会用,


把项目进程做成某一个进程的守护进程,这样即使项目因为意外原因挂掉,
主进程只要不挂监测到后,又会将项目进程再起起来


会随着主进程的结束,守护进程立刻结束!!!


使用场景:
写了一个多进程的python代码,把所有的子进程设成主进程的守护进程
这样主进程一关,所有设成守护进程的子进程也全部关闭了,这样节省计算机效率了!!!
需要做批量管理,一键关闭所有的进程,就可以把所有的子进程设成主进程的守护进程

--------------------------------------

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这个进程做成主进程的守护进程!!!
    p1.start()
    time.sleep(1)
    print('恕瑞玛皇帝:小吴勇嗝屁了')


守护进程关键字:   daemon
注意daemon这行代码,一定要在创建子进程的上面!!!不然就会报错!!!

image
.
image
.
.
.
.
.
.

僵尸进程与孤儿进程


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

----------------------------------------------

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

.
.
.
.
.
.
.

多进程数据错乱问题-------重要


模拟抢票软件所展示的数据不是实时的,你必须不停的刷新,让软件不停的请求服务端,
才能保证你的数据是实时的。所以你看到的数据是你当时查看的那一时刻的数据,
极限情况你查看到有一张票,当你点下单的时候,有一个人在你前面先下单买了,
这个时候按理说你应该是不能购票成功的!!!

--------------------------------------

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):    # 利用for循环,开10个进程!!!
        p = Process(target=run, args=('用户%s'%i, ))  # 传一个用户名编号到run函数里去
        p.start()

-------------------------------------------

因为for循环,几乎在同一时间创建出10个子进程,
 with open(r'data.json', 'r', encoding='utf8') as f:
        data = json.load(f)


10个进程都同时拿到了包含票数的文件,所以尽管有模拟网络延迟,
但是每个进程打开文件后,查询到的票数还是全部为1,所以全部都买票成功了,
很明显这样是不对的!!!

-----------------------------------------

多进程操作数据很可能会造成数据错乱!!!
必须要用互斥锁才能解决!!!

----------------------------------------

互斥锁的功能:    将并发变成串行!!!    牺牲了效率但是保障了数据的安全!!!

----------------------------------------

image
.
image
.
.
.
.
.
.
.
.

作业


1.将TCP服务端使用多进程实现并发效果
	聊天全部采用自动发送 不要用input手动输
2.整理今日内容及博客
3.查询IT行业可能出现的锁名称及概念
4.整理理论内容 尝试编写cs架构的软件 实现数据的上传与下载

.
.
.

posted @   tengyifan  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示