并发
在操作系统中,是一个时间段中有几个程序都处于运行状态,而且这几个程序都是在同一个处理机器上运行,但任一个时刻点上只有一个程序在处理机上运行。总结为,系统具有处理多个任务的能力。
并行
当系统有多个CPU时,线程的操作有可能非并发。当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行。总结为,系统具有同时处理多个任务的能力。
直接运行函数,函数a睡眠三秒钟,函数b睡眠2秒,整个流程需要五秒钟。
import time
def a():
print("a开始")
time.sleep(3)
print("a结束")
def b():
print("b开始")
time.sleep(2)
print("b结束")
if __name__ == '__main__':
t = time.time()
print(time.strftime("%H:%M:%S",time.gmtime(t)))
a()
b()
print("main")
print(time.strftime("%H:%M:%S", time.gmtime(time.time())))
02:21:10
a开始
a结束
b开始
b结束
main
02:21:15
开启多线程,同样函数a睡眠三秒钟,函数b睡眠2秒种,整个流程只需要三秒钟。
from threading import Thread
import time
def a():
print("a开始")
time.sleep(3)
print("a结束")
print(time.strftime("%H:%M:%S", time.gmtime(time.time())))
def b():
print("b开始")
time.sleep(2)
print("b结束")
print(time.strftime("%H:%M:%S", time.gmtime(time.time())))
if __name__ == '__main__':
t1 = Thread(target=a)
t2 = Thread(target=b)
print(time.strftime("%H:%M:%S", time.gmtime(time.time())))
t1.start()
t2.start()
print("main")
02:19:45
a开始
b开始
main
b结束
02:19:47
a结束
02:19:48
由于GIL锁的存在,python不存在并行,所谓的并行也只是宏观上并行微观上并发,本质上是由于遇到io操作不断的cpu切换,由于cpu切换速度极快,所以看起来就像是在同时执行。在IO密集型中,多线程是可以提升任务运行速度。但是遇到计算密集型时,串行反而可能更快
使用串行,耗时11秒
import time
def a():
num = 0
for i in range(10**8):
num+=1
print(num)
def b():
num = 0
for i in range(10**8):
num += 1
print(num)
if __name__ == '__main__':
t = time.time()
print(time.strftime("%H:%M:%S",time.gmtime(t)))
a()
b()
print("main")
print(time.strftime("%H:%M:%S", time.gmtime(time.time())))
02:37:41
100000000
100000000
main
02:37:52
使用多线程,耗时12秒
import time
from threading import Thread
def a():
num = 0
for i in range(10**8):
num+=1
print(num)
print(time.strftime("%H:%M:%S", time.gmtime(time.time())))
def b():
num = 0
for i in range(10**8):
num += 1
print(num)
print(time.strftime("%H:%M:%S", time.gmtime(time.time())))
if __name__ == '__main__':
t1 = Thread(target=a)
t2 = Thread(target=a)
print(time.strftime("%H:%M:%S", time.gmtime(time.time())))
t1.start()
t2.start()
print("main")
02:39:37
main
100000000
02:39:49
100000000
02:39:49
同步
当进程执行IO的时候,需要得到结果后才能继续执行
from concurrent.futures import ProcessPoolExecutor
import time
import os
def task(i):
print(f"{os.getpid()}开始")
time.sleep(1.5)
print(f"{os.getpid()}结束")
return i
if __name__ == '__main__':
pr = ProcessPoolExecutor()
for i in range(3):
obj = pr.submit(task,i)
print(obj.result())
pr.shutdown(wait=True)
print("=======")
# shutdown主线程等待子线程结束后再执行
2380开始
2380结束
0
3240开始
3240结束
1
7976开始
7976结束
2
=======
异步
当进程执行IO的时候,去执行其他任务,一直等到IO结束,再回来处理。
6796开始
6224开始
8712开始
6796结束
6224结束
8712结束
0
1
2
异步调用+回调函数
回调函数:按顺序接受每个任务的结果,进行下一步处理。
注意事项
- 进程池+回调: 回调函数由主进程去执行.
- 线程池+回调: 回到函数由空闲的线程去执行.
- 异步处理的IO类型.
- 回调处理非IO
示例:
import requests
from concurrent.futures import ThreadPoolExecutor
def task(url):
ret = requests.get(url)
if ret.status_code == 200:
return ret.text
def parce(ret): # 回调函数
print(len(ret.result()))
url_list = [
'http://www.baidu.com',
'http://www.JD.com',
'http://www.JD.com',
'http://www.JD.com',
'http://www.taobao.com',
'http://www.baidu.com',
'https://www.luffycity.com/',
'http://www.baidu.com',
'https://www.cnblogs.com/',
'https://www.sina.com.cn/'
]
if __name__ == '__main__':
t = ThreadPoolExecutor(5)
for i in url_list:
obj = t.submit(task,i)
obj.add_done_callback(parce)
阻塞
当程序遇到IO时进入阻塞状态,input
、join
、sleep
等都是阻塞。
非阻塞
程序运行中没有任何IO就是非阻塞