多任务处理是指用户可以在同一时间内运行多个应用程序,每个应用程序被称作一个任务.Linux、windows就是支持多任务的操作系统,比起单任务系统它的功能增强了许多。
当多任务操作系统使用某种任务调度策略允许两个或更多进程并发共享一个处理器时,事实上处理器在某一时刻只会给一件任务提供服务。因为任务调度机制保证不同任务之间的切换速度十分迅速,因此给人多个任务同时运行的错觉。多任务系统中有3个功能单位:任务、进程和线程。
线程基础
python的thread模块是比较底层的模块,python的threading模块是对thread做了一些包装的,可以更加方便的被使用
单线程执行
import time def test(): print("test...") time.sleep(1) if __name__ == '__main__': for i in range(5): test()
多线程执行
import time
import threading#调用线程模块
def sing():
'''唱歌5秒'''
for i in range(5):
print("---正在唱:菊花残")
time.sleep(1)
def dance():
'''跳舞5秒钟'''
for i in range(5):
print("---正在跳:菊花茶")
time.sleep(1)
def main():
t1 = threading.Thread(target=sing)#创建一个线程对象
t2 = threading.Thread(target=dance)
t1.start()#线程启动
t2.start()
if __name__ == '__main__':
main()
查看线程数量
threading.enumerate()函数
while True:
length = len(threading.enumerate())
print('当前运行线程数为:%d' %length)
if length<=1:
break
time.sleep(0.5)
线程的互斥锁
#创建锁
mutex = threading.Lock()
#锁定
mutex.acquire()
#释放
mutex.release()
注意:
- 如果这个锁之前没有上锁的,那么acquire不会阻塞
- 如果在调用acquire之前,其他线程已经调用则此线程会阻塞
使用多进程实现多任务
#使用多进程实现多任务
import threading
import time
import multiprocessing
def test1():
while True:
print("1----------")
time.sleep(1)
def test2():
while True:
print("2-------")
time.sleep(1)
def main():
p1 = multiprocessing.Process(target=test1)
p2 = multiprocessing.Process(target=test2)#与线程创建的区别
p1.start()
p2.start()
if __name__ == '__main__':
main()
协程中的多任务模块 greenlet
Greenlet是python的一个C扩展,来源于Stackless python,旨在提供可自行调度的‘微线程’, 即协程。generator实现的协程在yield value时只能将value返回给调用者(caller)。 而在greenlet中,target.switch(value)可以切换到指定的协程(target), 然后yield value。greenlet用switch来表示协程的切换,从一个协程切换到另一个协程需要显式指定。
在使用Greenlet 需要安装此模块
from greenlet import greenlet
import time
def test1():
while True:
print("12")
gr2.switch()
time.sleep(0.4)
def test2():
while True:
print("34")
gr1.switch()
time.sleep(0.4)
gr1 = greenlet(test1)
gr2 = greenlet(test2)
#切换到gr1中运行
gr1.switch()
执行结果:12
34
12
34
12
34
12
gevent 实现多任务
Gevent是python的第三方库,提供了比较完善的对协程的支持。Python中GIL的存在,导致多线程一直不是很好用,相形之下,协程的优势就更加突出了。
Gevent的基本思想是:当遇到IO操作时,会自动写换到其他gevent,再在适当的时间切回来继续执行。这样就减少了IO操作时的等待耗时,从而能够提高硬件资源的利用率
import gevent
def f(n):
for i in range(n):
print(gevent.getcurrent(), i)
g1 = gevent.spawn(f,5)
g2 = gevent.spawn(f,5)
g3 = gevent.spawn(f,5)
g1.join()
g2.join()
g3.join()
D:\pcode\venv\Scripts\python.exe D:/pcode/201905/gevent_511.py
<Greenlet at 0x20221dcebf8: f(5)> 0
<Greenlet at 0x20221dcebf8: f(5)> 1
<Greenlet at 0x20221dcebf8: f(5)> 2
<Greenlet at 0x20221dcebf8: f(5)> 3
<Greenlet at 0x20221dcebf8: f(5)> 4
<Greenlet at 0x2022219f048: f(5)> 0
<Greenlet at 0x2022219f048: f(5)> 1
<Greenlet at 0x2022219f048: f(5)> 2
<Greenlet at 0x2022219f048: f(5)> 3
<Greenlet at 0x2022219f048: f(5)> 4
<Greenlet at 0x2022219f158: f(5)> 0
<Greenlet at 0x2022219f158: f(5)> 1
<Greenlet at 0x2022219f158: f(5)> 2
<Greenlet at 0x2022219f158: f(5)> 3
<Greenlet at 0x2022219f158: f(5)> 4
Process finished with exit code 0
Gevent 遇到延时才切换,注意不是time.sleep 而是gevent.sleep()
import gevent
import time
def f1(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
def f2(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
def f3(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5) #不会引起真正延时,还是在一个函数中
print("1 ---- start")
g1 = gevent.spawn(f1,5)
print("2-------start")
g2 = gevent.spawn(f2,5)
print("3-------start")
g3 = gevent.spawn(f3,5)
print("4--------start")
g1.join()
g2.join()
g3.join()
运行结果:
D:\pcode\venv\Scripts\python.exe D:/pcode/201905/gevent_511.py
1 ---- start
2-------start
3-------start
4--------start
<Greenlet at 0x223900aebf8: f1(5)> 0
<Greenlet at 0x223900aebf8: f1(5)> 1
<Greenlet at 0x223900aebf8: f1(5)> 2
<Greenlet at 0x223900aebf8: f1(5)> 3
<Greenlet at 0x223900aebf8: f1(5)> 4
<Greenlet at 0x223903fe048: f2(5)> 0
<Greenlet at 0x223903fe048: f2(5)> 1
<Greenlet at 0x223903fe048: f2(5)> 2
<Greenlet at 0x223903fe048: f2(5)> 3
<Greenlet at 0x223903fe048: f2(5)> 4
<Greenlet at 0x223903fe158: f3(5)> 0
<Greenlet at 0x223903fe158: f3(5)> 1
<Greenlet at 0x223903fe158: f3(5)> 2
<Greenlet at 0x223903fe158: f3(5)> 3
<Greenlet at 0x223903fe158: f3(5)> 4
Process finished with exit code 0
若改为gevent.sleep(0.5)
则会出现交替执行
总结:进程,线程,协程
1、多进程一般使用multiprocessing库,来利用多核CPU,主要是用在CPU密集型的程序上,当然生产者消费者这种也可以使用。多进程的优势就是一个子进程崩溃并不会影响其他子进程和主进程的运行,但缺点就是不能一次性启动太多进程,会严重影响系统的资源调度,特别是CPU使用率和负载。
2、多线程一般是使用threading库,完成一些IO密集型并发操作。多线程的优势是切换快,资源消耗低,但一个线程挂掉则会影响到所有线程,所以不够稳定。现实中使用线程池的场景会比较多
3、协程一般是使用gevent库,当然这个库用起来比较麻烦,所以使用的并不是很多。相反,协程在tornado的运用就多得多了,使用协程让tornado做到单线程异步,据说还能解决C10K的问题。所以协程使用的地方最多的是在web应用上。