首页 > 系统相关 >【Python多任务--进程,协程】

【Python多任务--进程,协程】

时间:2022-12-15 17:32:27浏览次数:53  
标签:__ 协程 Python -- range time print work1

一、进程

进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
进程是线程的容器,一个进程可以有多个线程

进程特征

  • 动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。
  • 并发性:任何进程都可以同其他进程一起并发执行。
  • 独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位。
  • 异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进。

二、python多进程

创建多进程的两种方式:

  • 调用multiprocessing.Process模块
  • 重写multiprocessing.Process类的run方法

1、调用multiprocessing.Process模块创建进程

Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)

强调:

  1. 需要使用关键字的方式来指定参数
  2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
参数介绍

1 group参数未使用,值始终为None
2 target表示调用对象,即子进程要执行的任务
3 args表示调用对象的位置参数元组,args=('liang',)
4 kwargs表示调用对象的字典,kwargs={'name':'anne','age':18}
5 name为子进程的名称

from multiprocessing import Process

def work1(name):
for i in range(4):
time.sleep(1)
print(f'{name}浇花的第{i + 1}秒')

def work2(name):
for i in range(3):
time.sleep(1)
print(f'{name}打墙的第{i + 1}秒')


if __name__ == '__main__':
p1 = Process(target=work1, args=('liang',))
p2 = Process(target=work2, args=('小狼',))

p1.start()
p2.start()

p1.join()
p2.join()
print('主线程执行完毕')

#输出
liang浇花的第1秒
小狼打墙的第1秒
小狼打墙的第2秒
liang浇花的第2秒
liang浇花的第3秒
小狼打墙的第3秒
liang浇花的第4秒
主线程执行完毕

2、重写multiprocessing.Process类的run方法

将要执行的任务写入run方法,同样的任务可以多线程并行执行

import time
from multiprocessing import Process

class MyProcess(Process):
"""自定义的进程类"""
def __init__(self, name):
super().__init__()
self.name = name

def run(self):
for i in range(3):
time.sleep(1)
print(f'{self.name}浇花的第{i + 1}秒')
if __name__ == '__main__':

q_list = []
for i in range(2): #创建4个线程并启动
p = MyProcess(f"liang{i}")
q_list.append(p)
p.start()
for q in q_list: #等待启动的线程执行结束
q.join()
print('主进程执行完毕')

#输出
liang0浇花的第1秒
liang1浇花的第1秒
liang0浇花的第2秒
liang1浇花的第2秒
liang0浇花的第3秒
liang1浇花的第3秒
主进程执行完毕

注意: 多进程 执行,必须在main函数下, if __name__ == '__main__':

三、多进程之间通讯

多进程之间所有资源都是独立的,不能共享全局变量

queue.Queue模块只能在一个进行中使用,可以实现一个进程中的多个线程相互通讯

多个进程之间的相互通讯,需要用到--multiprocessing.Queue:可以多个进程之间共用(通讯)


"""
进程之间通信:使用队列
multiprocessing.Queue:可以多个进程之间共用(通讯)
queue.Queue模块只能在一个进行中使用,一个进程中多个线程使用
"""
from multiprocessing import Process, Queue

def work1(q):
for i in range(5000):
n = q.get()
n += 1
q.put(n)

print("work1结束时候n的值:", n)


def work2(q):
for i in range(5000):
n = q.get()
n += 1
q.put(n)
print("work2结束时候n的值:", n)


if __name__ == '__main__':
q = Queue()
q.put(100)
p1 = Process(target=work1, args=(q,))
p2 = Process(target=work2, args=(q,))

p1.start()
p2.start()

p1.join()
p2.join()
print('两个子进程执行结束之后,主进程打印的n:', q.get())

#输出
work2结束时候n的值: 10090
work1结束时候n的值: 10100
两个子进程执行结束之后,主进程打印的n: 10100


===================================================================================================================================================================================================================================================

接上文,我们下面来讲一下:

python多任务--协程

一、前言

协程

协程 ,又称为微线程,它是实现多任务的另一种方式,只不过是比线程更小的执行单元。因为它自带CPU的上下文,这样只要在合适的时机,我们可以把一个协程切换到另一个协程。

协程的优势

  • 执行效率高,因为子程序切换函数,而不是线程,没有线程切换的开销,由程序自身控制切换。于多线程相比,线程数量越多,切换开销越大,协程的优势越明显
  • 不需要锁的机制,只有一个线程,也不存在同时写变量冲突,在控制共享资源时也不需要加锁。

二、实现协程的几种方式

1、yield(生成器)可以很容易的实现从一个函数切换到另外一个函数

def work1():
for i in range(5):
print(f"work1--befor----{i}")
yield
print(f"work1--after----{i}")
time.sleep(0.5)

def work2():
for i in range(5):
print(f"work2---befor---{i}")
yield
print(f"work2--after----{i}")
time.sleep(0.5)

def main():
g1 = work1()
g2 = work2()
while True:
try:
next(g1)
print('主程序')
next(g2)
except StopIteration:
break
main()

运行结果如下:

【Python多任务--进程,协程】_子进程


2、原生的协程

import asyncio
# 定义一个协程函数
async def work1():
for i in range(10):
print(f"work1--浇花----{i}")

# 调用协程函数,返回的是一个协程对象
cor1 = work1()

# 执行协程
asyncio.run(cor1)
2.1、使用原生的协程实现多任务(不同任务)

协程中切换,通过await语法来挂起自身的协程。await后面跟上耗时操作,耗时操作一般指IO操作: 网络请求,文件读取等,使用asyncio.sleep模拟耗时操作。协程的目的也是让这些IO操作异步化。
sleep()需要用asyncio.sleep()
await必须要在 async def function(): 中用,否则会报错

import asyncio
async def work1():
for i in range(3):
print(f"work1--浇花----{i}")
await asyncio.sleep(1)

async def work2():
for i in range(5):
print(f"work2--打墙----{i}")
await asyncio.sleep(1)

if __name__ == '__main__':
loop = asyncio.get_event_loop()
# 创建两个协程任务
tasks = [
work1(),
work2(),
]
# 启动事件循环并将协程放进去执行
loop.run_until_complete(asyncio.wait(tasks))

#输出
work1--浇花----0
work2--打墙----0
work1--浇花----1
work2--打墙----1
work1--浇花----2
work2--打墙----2
work2--打墙----3
work2--打墙----4
2.2、使用原生的协程实现多任务(同一方法处理大量数据)
import asyncio
from queue import Queue
import time

def decorator(func):
def wrapper():
# 函数执行之前获取系统时间
start_time = time.time()
func()
# 函数执行之后获取系统时间
end_time = time.time()
print('执行时间为:', end_time - start_time)
return end_time - start_time
return wrapper

async def work1(q):
while q.qsize():
print(f"请求url:{q.get()}")
await asyncio.sleep(0.1)

@decorator
def main():
#创建一个包含有1000条url的队列
q = Queue()
for i in range(1000):
q.put(f"www.baidu.com.{i}")

loop = asyncio.get_event_loop()
# 创建100个协程任务
tasks = [work1(q) for i in range(100)]

# 启动事件循环并将协程放进去执行
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

if __name__ == '__main__':
main()

#输出
...
请求url:www.baidu.com.890
请求url:www.baidu.com.891
请求url:www.baidu.com.892
...
执行时间为: 1.060093641281128

100个协程执行1000个耗时0.1秒的请求只需要1秒

2.3、版本区别:

python 3.7 以前的版本调用异步函数的步骤:(如以上代码)

  • 1、调用asyncio.get_event_loop()函数获取事件循环loop对象
  • 2、通过不同的策略调用loop.run_forever()方法或者loop.run_until_complete()方法执行异步函数

python3.7 以后的版本

  • 1、asyncio.run() 函数用来运行最高层级的入口点,下例的main()函数。此函数总是会创建一个新的事件循环并在结束时关闭之。它应当被用作 asyncio 程序的主入口点,理想情况下应当只被调用一次。
  • 2、await 等待一个协程,也可以启动一个协程。
  • 3、asyncio.create_task() 函数用来并发运行作为 asyncio 任务 的多个协程。下例并发运行两个work协程

改动后代码如下


#以上省略
async def main():
q = Queue()
for i in range(1000):
q.put(f"www.baidu.com.{i}")
#创建了任务
tasks = [asyncio.create_task(work1(q)) for i in range(100)]
#将任务丢到执行队列里面去
[await t for t in tasks]

if __name__ == '__main__':
m=main()
start_time = time.time()
asyncio.run(m)
end_time = time.time()
print('运行时间{}秒'.format(end_time - start_time))

3、greenlet模块


import time
import greenlet

"""
greenlet:在协程之间只能手动进行切换
"""
def work1():
for i in range(6):
time.sleep(1)
cor2.switch()
print(f'浇花的第{i + 1}秒')

def work2():
for i in range(5):
time.sleep(1)
cor1.switch()
print(f'打墙的第{i + 1}秒')

cor1 = greenlet.greenlet(work1)
cor2 = greenlet.greenlet(work2)
cor1.switch()

4、gevent模块实现多任务

  • gevent模块对greenlet又做了一层封装,当程序遇到IO耗时等待的时候会进行自动切换
  • gevent中默认是遇到gevent.sleep()会自动进行切换
  • 如果让gevent遇到io耗时自动切换:需要在程序的导包处加一个补丁monkey.patch_all(),该补丁不支持多线程
from gevent import monkey
monkey.patch_all()
import gevent

def work1():
for i in range(6):
gevent.sleep(1)
print(f'浇花的第{i + 1}秒')


def work2():
for i in range(5):
gevent.sleep(1)
print(f'打墙的第{i + 1}秒')


# 创建两个协程
g1 = gevent.spawn(work1)
g2 = gevent.spawn(work2)

# 等待所有协程任务运行完毕
gevent.joinall([g1, g2])

示例: 模拟50000个协程执行对100000个地址的请求

from gevent import monkey
monkey.patch_all()
import gevent
import time

#创建100000个地址
urls = ['http://www.baidu.com' for i in range(100000)]

#定义需要执行的任务函数
def work():
while urls:
url = urls.pop()
# res = requests.get(url)
time.sleep(0.5)
print(f"正在请求url:{url},请求结果:url")

def main():
cos = []

#创建50000个协程
for i in range(50000):
cor = gevent.spawn(work)
cos.append(cor)

# 等待所有协程任务运行完毕
gevent.joinall(cos)

main()


标签:__,协程,Python,--,range,time,print,work1
From: https://blog.51cto.com/u_15874356/5938744

相关文章

  • 【Python多任务--进程池Pool】
    进程池Pool在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间。当被操作对象数目不大时,可以直接利用multiprocess......
  • 基于云原生的集群自愈系统 Flink Cluster Inspector
    作者:舟柒、楼台1.业务背景与挑战1.1实时计算集群现状关于热点机器处理一直是阿里云Flink集群运维的一大痛点,不管在日常还是大促都已经是比较严重的问题,同时这也是分布......
  • SAP ABAP CDS view 里 INNER JOIN 和 Association 的区别
    最近有朋友在我的[知识星球]里向我提问,SAPABAPCDSview的INNERJOIN和Association的功能可以理解为一样吗?本文就来聊一聊这个话题。既然CDSview里同时支持了INN......
  • 安装vue-element-admin启动报错error:0308010C:digital envelope routines::unsupport
    这个,一般都是,node.js的版本匹配问题造成的。后面我通过安装nvm,在nvm下重新安装了一个低版本的Node.js才搞定。1、安装nvm管理工具(先关掉360等软件,不然会弹出警告!)从官网......
  • 3.PL/SQL简介
    1.PL/SQL简介特点:支持SQL语句命令和操作支持SQL中的数据类型,还扩展了一些自己特有的类型,如:type类型支持SQL中的内置函数和运算符PL/SQL支持事务,对写好的PL/SQL程序(过......
  • 决策分支
    if对于代码来说,最基本的决策就是使用if从句if(condition){//whattodoifconditionismet}elseif(condition){//whattodoifconditionismet}else{......
  • 4.PL/SQL块
    1.PL/SQL块语法结构[declare]申明语句begin执行语句[exception]异常处理语句end2.语法解析申明部分:可选部分,由declare开始,声明执行部分所需要的......
  • ATA驱动- hardreset&softreset分析
    内核文档内核路径下的文档Documentation/DocBook/libata.tmpl内核文档解释说明不同的reset调用对应的底层Errorhandlerreset函数;softreset&hardresetEH函数在内核a......
  • 5.PL/SQL数据类型
    1.标量数据类型变量数据类型的变量只有一个值,且内部没有分量数值类型:用于存储数值类型的数据。如:number:可以存储小数和整数类型数据。格式为:number(p,s):p表示长度,s表......
  • c# WinForm--微信Native支付
    一、了解Native支付流程我用的是模式二,模式二的流程如下二、如何将Demo用在我的WinForm里面打开Demo工程,复制里面的lib、third文件夹到我的工程下,添加引用,引用的内容......