假装使用论文格式书写,不喜勿喷,有不对的地方可以指正一下,多谢。
Python实现多任务
幽靈ghost
摘要:使用单任务来完成延时操作比较多的程序的话,而单任务程序还是顺序执行的,这样会大大延长程序完成的时间。那么这样想,若是在一个大的任务里有一个个有延时操作的小任务,那利用等待的时间来完成其它的任务岂不是可以大大地缩短程序的完成时间。多任务可以通过进程、线程与协程来完成,三者耗费的资源依次递减。
关键字:多任务、线程、进程 、协程
引言:在python中大部分的函数调用都是通过单任务实现的,如下面的两个函数,这时候调用函数只能等sing()函数结束才能再调用dance()函数,这种就是单任务,任务是一步一步进行的。
import time
def sing():
for i in range(5):
print("------正在唱歌------")
time.sleep(1)
def dance():
for i in range(5):
print("------正在跳舞------")
time.sleep(1)
sing()
dance()
实现多任务有三个方法,通过创建多进程、多线程以及多协程都能实现多任务。
目录
一、进程实现多任务
进程:是操作系统分配资源的基本单位,如对音响,摄像头等资源的分配。
创建多进程类似于微信、QQ的多开,耗费的资源最大。
1、创建多进程
下面是通过创建多进程实现多任务的方法,这时test1与test2会交替打印,加上time.sleep(1)是为了进行延时操作,使结果更加明显,不然系统的执行速度太快,会导致打印结果同单任务一样。
import multiprocessing
import time
def test1(n):
while True:
print("---%d---" % n)
time.sleep(1)
def test2(n):
while True:
print("---%d---" % n)
time.sleep(1)
def main():
"""
在该步骤并不会创建进程,只是创建了一个可以创建进程的对象
target指定函数名,既要调用的函数
可以传递参数给函数,注意args传递过去的参数是一个元组
"""
p1 = multiprocessing.Process(target=test1, args=(1, ))
p2 = multiprocessing.Process(target=test2, args=(2, ))
# 当调用了进程对象的start()方法后,则进程被创建
p1.start()
p2.start()
if __name__ == "__main__"
main()
2、Queue实现多进程间的数据共享
多进程之间的数据是不会共享的,这时候需要使用multiprocessing提供的Queue()方法来实现多进程之间的数据共享。
q.put():向队列中写入数据,队列的数据是先进先出
q.get():从队列中取出数据
q.empty():判断队列是否为空,空则返回True,非空返回False
q.full():判断队列是否为满,满则返回True,不满返回False
def download(q):
"""模拟数据的下载"""
data = [11, 22, 33]
# q.put()向队列写入数据
for temp in data:
q.put(temp)
print("数据下载完成")
def analysis(q):
"""模拟数据的分析"""
time.sleep(1)
analysis_list = list()
# 从队列中获取数据
while not q.empty():
data = q.get()
analysis_list.append(data)
print(analysis_list)
def main():
"""
创建一个队列,括号里写几表示可以保存多少数据
不写的话表示保存默认最大的数据(由自身内存决定)
"""
q = multiprocessing.Queue()
p1 = multiprocessing.Process(target=download, args=(q,))
p2 = multiprocessing.Process(target=analysis, args=(q,))
p1.start()
p2.start()
3、进程池
import time, os, random
import multiprocessing
def worker(msg):
t_start = time.time()
print("%s开始执行,进程号为%d" % (msg, os.getpid()))
# random.random()随机生成0~1之间的浮点数
time.sleep(random.random()*2)
t_stop = time.time()
print(msg,"执行完毕,耗时%0.2f" % (t_stop-t_start))
def main():
# 定义一个进程池,最大进程数为3
po = multiprocessing.Pool(3)
for i in range(0, 10):
# multiprocessing.Pool().apply_async(要调用的目标名,(传递给目标的参数元组))
# 每次循环将会用空闲出来的子进程去调用目标
po.apply_async(worker, args=(i,))
print("---start---")
po.close() # 关闭进程池,关闭后po不再接收新的请求
po.join() # 等待po中所有子进程执行完毕,必须放在close语句之后
print("---stop---")
if __name__ == "__main__":
main()
二、线程实现多任务
线程:是任务调度和执行的基本单位,包含在进程里。
创建多线程来实现多任务,耗费的资源比创建多进程来实现多任务少。
多线程就是在同一个进程下本来只有是只有一个主线程在进行的,这时候又生成了一些子线程去执行任务,主线程会等子线程执行完任务再销毁,如果主线程先销毁,那么子线程也会随之销毁。
1、创建多线程的两种方法
(1)通过函数名创建实例化对象
这种方法来创建多线程会比较方便,我个人比较喜欢这个方法
import threading
import time
def sing():
for i in range(5):
print("------正在唱歌------")
time.sleep(1)
def dance():
for i in range(5):
print("------正在跳舞------")
time.sleep(1)
def main():
# 这两个语句只是创建了一个对象,并不会创建线程
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=dance)
# 只有调用start时候线程才会被创建
t1.start()
t2.start()
if __name__ == "__main__":
main()
(2)通过继承类来实现多线程
继承threading.Thread类,再通过自己创建的类来创建多线程,注意,继承类时必须要写run()方法
class MyThread(threading.Thread):
def run(self):
time.sleep(1)
print(threading.enumerate()) # enumerate()方法可以查看现在的线程数
for i in range(3):
msg = "I' m" + self.name + '@' + str(i) # name属性中保存的说当前线程的名字
print(msg)
def main():
t = MyThread()
t.start() # 还是通过start()方法来调用
2、通过互斥锁来解决资源竞争
g_num = 0 # 多线程之间共享全局变量,但共享全局变量有时会产生资源竞争问题
def test1(num):
global g_num
for i in range(num):
mutex.acquire() # 上锁
g_num += 1
mutex.release() # 解锁
def test2(num):
global g_num
for i in range(num):
mutex.acquire()
g_num += 1
mutex.release()
# 创建一个互斥锁,默认是没有上锁的,在会产生资源竞争的地方上锁,上锁的地方(代码)越小越好
mutex = threading.Lock()
def main():
t1 = threading.Thread(target=test1, args=(100000, ))
t2 = threading.Thread(target=test2, args=(100000, ))
t1.start()
t2.start()
time.sleep(2) # 先让主线程进行延时操作,等子线程进行计算
print(g_num)
三、协程协实现多任务
协程:协程是一种比线程更加轻量级的存在。协程完全由程序所控制(在用户态执行),带来的好处是性能大幅度的提升。协程并没有增加线程数量,只是在线程的基础之上通过分时复用的方式运行多个协程。
1、使用协程实现多任务
import gevent, time
def f(n):
for i in range(n):
print(gevent.getcurrent(), i)
# time.sleep(0.5) # 使用time来进行延时是做不到任务切换的
gevent.sleep(0.5) # 得用gevent自己的延时才会进行任务切换
def main():
# 利用等待的时间去做其他任务
g1 = gevent.spawn(f, 5)
g2 = gevent.spawn(f, 5)
g3 = gevent.spawn(f, 5)
g1.join()
g2.join()
g3.join()
if __name__ == '__main__':
main()
2、为gevent打补丁
在上面的例子可以看到,如果使用gevent的话,使用time进行延时是做不到任务切换的,这就变成单任务了,这时候我们就得使用gevent为我们提供的补丁,使得time.sleep()进行延时可以完成任务切换,在以后使用gevent实现多任务时,我的建议是每次都把补丁给写上。
import gevent, time
from gevent import monkey
monkey.patch_all()
def f(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
def main():
# 利用等待的时间去做其他任务
# 一般使用gevent.joinall,可以少写几句话[doge]
gevent.joinall([
gevent.spawn(f, 5),
gevent.spawn(f, 5),
gevent.spawn(f, 5)
])
if __name__ == '__main__':
main()
参考文献:
(1)【python教程】多任务之进程、线程、协程之间的区别与联系_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV19x411R7rG?p=1