一. 进程
1. 同步异步概念
异步:指 主进程和子进程同时执行 分配一个子进程就去执行一个
同步:指分配一个子进程时,必须等到这个子进程执行完才会继续分配写一个子进程任务然后执行....
当p.start下面 写入 p.jion时 主程序会阻塞在这里让没有完成的子进程继续执行 主进程必须等待 子进程执行完才执行下一 步 p.daemon=True 设置 守护进程 子进程会随主进程结束而结束 ,设置在p.start()之前
2.阻塞 例如 程序运行时遇到input , while True: 等等
2.多进程
速度慢于pool.map 快于同步pool.apply
from multiprocessing import Process
import time
def func(num):
time.sleep(0.5)
print(num)
if __name__ == '__main__':
l=[]
start_time = time.time()
for i in range(50):
p = Process(target=func, args=(i,))
p.start()
l.append(p)
[p.join() for p in l]
print(time.time() - start_time)
[p.join() for p in l] 将主进程阻塞 一次性最多执行的个数取决于cpu 一直在开进程
主进程等待所有子进程执行完, 而pool.apply是下一个要执行的进程等待上一个没有执行完的任务每一个都要等所以非常慢
3.进程池 的几个方法
最佳开启进程数 os.cpu_count()+1 一次性最多执行的个数
1.pool.map
参数: 函数,可迭代对象 速度慢于异步
pool.map 一次性执行进程池中的个数个进程 可以不添加pool.close 和pool.join 主进程也会等待子进程执行完才结束
但执行顺序是混乱的 最好加上
返回值 ret=p.map ( func, [ ] ) 为一个列表 值为func 的返回值 没有默认为None
运行时间0.900
from multiprocessing import Pool
import time
def func(num):
time.sleep(0.5)
print(num)
if __name__ == '__main__':
start_time = time.time()
p = Pool(5)
p.map(func,[i for i in range(50)])
print(time.time()-start_time)
2. pool.apply
一种默认同步的方法 速度最慢 进程池中的进程 都不是不是守护进程 每一个任务都要等待执行完
参数:函数,args=( ) 元组
同步:我的池中即使有五个任务,也是一个任务一个任务的去执行上一个任务结束才执行下一个
原因:主进程会等待所有子进程执行完才继续往下执行 不用加 join
返回值: 就是执行函数的 返回值
from multiprocessing import Pool
import time
def func(num):
time.sleep(0.5)
print(num)
if __name__ == '__main__':
start_time=time.time()
pool = Pool(5)
for i in range(50):
pool.apply(func, args=(i,))
print(time.time()-start_time)
3.pool.apply_async
一种异步的方法 ,速度极快 必须加pool.close() pool.join 等待pool执行完
原因:进程池中的进程是守护进程,当主函数执行完时,而子程序没有执行完时时,子程序会被强制结束
参数:函数,args=( ) 元组,callback=
异步:我的池中有五个任务,就处理五个任务,接下来哪个任务处理完了,就马上去接受下一个任务
返回值: 为一个对象 使用ret.get() 可以获得值,但是只能一个一个get 速度变慢
from multiprocessing import Pool
import time
def func(num):
time.sleep(0.5)
print(num)
if __name__ == '__main__':
start_time = time.time()
pool = Pool(5)
for i in range(50):
pool.apply_async(func, args=(i,))
pool.close()
pool.join()
print(time.time() - start_time)
二.pool.apply_async 之回调函数
下图输出1-9,
原因: pool.apply_async中的第一个函数的return值 会默认返回到指定的callback函数中去当作形参接收
from multiprocessing import Pool
import requests
url='https://www.baidu.com'
def func(url):
r=requests.get(url)
if r.status_code ==200:
return url,r.text
def callback(item):
url,page=item
print(item)
if __name__ == '__main__':
pool = Pool(5)
for i in range(1):
pool.apply_async(func, args=(url,),callback=callback)
pool.close()
pool.join()
注意 :回调函数是由主进程调用的,而不是子进程,子进程只负责把返回结果给到回调函数
所以回调函数执行的顺序是乱的 例如:
from multiprocessing import Pool
import requests
def func(url):
print(url)
r=requests.get(url)
if r.status_code ==200:
return url,r.text
def callback(item):
url,page=item
print(url)
if __name__ == '__main__':
urls=[
'https://www.dytt8.net/',
'https://www.dy2018.com/html/gndy/',
'http://desk.zol.com.cn/youxi/yingxionglianmeng/1920x1080/',
'https://www.douyu.com/directory/myFollow'
]
pool = Pool(5)
for url in urls:
pool.apply_async(func, args=(url,),callback=callback)
pool.close()
pool.join()
输出结果为:
https://www.dytt8.net/
https://www.dy2018.com/html/gndy/
http://desk.zol.com.cn/youxi/yingxionglianmeng/1920x1080/
https://www.douyu.com/directory/myFollow
http://desk.zol.com.cn/youxi/yingxionglianmeng/1920x1080/
https://www.douyu.com/directory/myFollow
https://www.dy2018.com/html/gndy/
https://www.dytt8.net/
4. 另一个模块的进程池 -异步--ProcessPoolExecutor
from concurrent.futures import ProcessPoolExecutor
import time
def fun(i):
time.sleep(1)
return i
if __name__ == '__main__':
p = ProcessPoolExecutor(5)
# for i in range(50):
# r=p.submit(fun,i)
# print(r.result())
r = p.map(fun, [i for i in range(50)])
p.shutdown()
for i in r:
print(i)
print(r.__next__())
这两种方式与下面的线程池类似
二.线程
1.并发,并行
并发:就是同时做多件事情。
并行:就是把正在执行的大量任务分割成小块,分配给多个同时运行的线程。
一般情况下,为了让CPU充分利用,并行处理都会采用多线程。
2. 线程与进程比较
开启很多进程时很慢,开启很多线程很快
# 守护线程是根据主线程执行结束才结束
# 守护线程不是根据主线程的代码执行结束而结束
# 主线程会等待普通线程执行结束,再结束
# 守护线程会等待主线程结束,再结束
# 所以,一般把不重要的事情设置为守护线程
# 守护进程是根据主进程的代码执行完毕,守护进程就结束
def func():
time.sleep(2)
print(123)
def func1():
time.sleep(5)
print('abc')
if __name__ == '__main__':
t = Thread(target=func)
t.daemon = True
t.start()
t1 = Thread(target=func1)
t1.start()
print(99999999999999999999)
以上输出: 先打印9999...在打印123,在打印abc
原因:# 守护线程是根据主线程执行结束才结束 守护线程不是根据主线程的代码执行结束而结束
当执行到9999时代码已经执行完, 此时如果是守护进程会随代码结束而结束, 而守护线程会随程序结束而结束
而func1不是守护线程 所以程序没有结束 所以先打印123,在打印abc
二.线程池 ---另一个模块--ThreadPoolExecutor
from concurrent.futures import ThreadPoolExecutor
from time import sleep
import time
def func(i):
sleep(1)
# print(i)
return i
t=ThreadPoolExecutor(25)
for i in range(200):
a=t.submit(func,i)
print(a.result())
t.shutdown()
第二种和第一种一样,只是拿结果的时候不同 这里的map和进程池中的map返回结果不一样
start_time = time.time()
r=t.map(func,[i for i in range(50)]) 这里是一个生成器对象,而进程池的map是一个列表
t.shutdown()
for i in r: 用print(r.__next__()) 也可以一个一个拿
print(i)
print(time.time()-start_time)
在拿返回值的时候 第一种 就像同步一样,必须等到上一个拿到返回值才会去做下一个
而第二个不会影响异步的效果
也有一个回调函数
from concurrent.futures import ThreadPoolExecutor
from time import sleep
import time
def func(i):
sleep(2)
return i
def call(i):
print(i.result())
t=ThreadPoolExecutor(25)
for i in range(200):
a=t.submit(func,i).add_done_callback(call)
t.shutdown()
五.总结
一.在for 循环中 只要接收 了线程的返回值 就必须等待 拿到这个接受的值,下一个线程或进程才会继续,,而用map方法不会这样
多进程中 from multi processing import Pool
1. pool.map 返回值是一个列表 没有回调 函数 需要加 close join
2.pool.apply 返回值就是 调用函数的返回值 没有回调 函数 不需要 close join
3.pool.apply_async 返回值是 一个对象 用 get() 获取 回调函数 callback=func() 接受被调用函数的返回值 一个元组
线程池 需要close join
多线程中 from threading import Thread
1.普通多线程 取不到返回值 没有回调函数 需要 join
二.两个异步的进程池线程池
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
用submit(func,i) 提交任务
for i in range(200):
a=t.submit(func,i) 这里接受了 返回值 所以变慢 返回值用 result() 获取
print(a.result())
t.shutdown() 相当于 close(),join()
两个都有map方法
r=t.map(func,[i for i in range(50)])
t.shutdown()
for i in r: 返回值是yield 那样的 生成器 generator 可以遍历
print(i) #print(r.__next__()) 一个一个拿
两个都有回调函数
for i in range(200):
a=t.submit(func,i).add_done_callback(call)
t.shutdown()