线程池
concurrent.futures模块提供了一个用于异步执行callables的高级接口。
concurrent.futures.ThreadPoolExecutor
submit(fn,* args,** kwargs )
将可调用的fn调度为执行, 并返回表示可调用执行的对象
from concurrent.futures import ThreadPoolExecutor
def fun(i):
print('my name is %s'%i)
t = ThreadPoolExecutor(5)
for i in range(10):
ret = t.submit(fun, i)
#返回一个可执行的对象 <Future at 0x7f2da90c3e10 state=pending>
**shutdown(wait=True) **
相当于进程池的pool.close()+pool.join()操作 wait=True,等待池内所有任务执行完毕回收完资源后才继续 wait=False,立即返回,并不会等待池内的任务执行完毕 但不管wait参数为何值,整个程序都会等到所有任务执行完毕 submit和map必须在shutdown之前
具体代码
def fun(i):
print('my name is %s'%i)
return i*2
t = ThreadPoolExecutor(5)
for i in range(10):
ret = t.submit(fun, i)
print(ret.result()) #查看返回的结果
t.shutdown() #表示线程都执行完毕
print('主进程结束')
map 取代for循环submit的操作
from concurrent.futures import ThreadPoolExecutor
def fun(i):
print('my name is %s'%i)
return i*2
t = ThreadPoolExecutor(5)
res = map(fun, range(20))
for i in res:print(i)
ProcessPoolExecutor
它是Executor使用的过程池异步执行调用子类。 ProcessPoolExecutor使用该multiprocessing模块,它允许它侧向全局解释器锁定,但也意味着只能执行和返回可选对象。 该main模块必须可由工作程序子进程导入。这意味着ProcessPoolExecutor在交互式解释器中不起作用。
简单的回调函数
from concurrent.futures import ProcessPoolExecutor
def fun(i):
print(i)
return i*2
def call_back(arg):
print('call back:', arg.result())
if __name__ == '__main__':
p = ProcessPoolExecutor()
for i in range(20):
p.submit(fun, i).add_done_callback(call_back)
print('主线程')
××××××××××××××××××××××××××××××××××××××
协程
协程的优势:
线程: 计算机中能被cpu执行的最小单位
一条线程在多个任务之间来回切换,切换这个动作是浪费时间的
对与cpu, 操作系统来说协程是不存在的# 一个任务不得不陷入阻塞, 在这个任务的过程中切换到另一个任务继续执行
你的程序的只要还需要执行, 你的当前线程永远不会阻塞
同时做几件事件,在做一件事件的时候还可以做其他的事件,比如做饭的时候,煮粥的时候可以切菜,炒菜,干一些其他事件,等待煮粥事件
合理的利用时间, 不要等待一个事件, 比如程序的时候,在阻塞的时候干一些其他啊的事件
协程模块:
利用协程来进行多个任务陷入阻塞的时候进行切换来保证在处理多个任务的时候总是忙
无论是进程还是线程都是有操作系统来切换的,开启的线程,进程会给操作系统的调动带来压力
如果我们使用的是协程,协程在程序之间的切换操作系统感知不到,无论开启多个协程对操作系统的调用不会带来压力
spawn(函数名) 产生了一个协程任物在遇到IO操作的时候帮助我们在多任务之> 间自动切换 join() 阻塞 直到某个任务被执行完毕 join_all() value属性 获取返回值
示例代码
from gevent import monkey
monkey.patch_all() #d
import threading
import time
import gevent
def fun1():
print('1111')
gevent.sleep(1)
print('2222')
g1_id = threading.current_thread().getName()
print(g1_id)
def fun2():
print('3333')
gevent.sleep(1)
print('44444')
#查看进程ID
g2_id = threading.current_thread().getName()
print(g2_id) #假的线程
g1 = gevent.spawn(fun1)#遇到阻塞就回切换
g2 = gevent.spawn(fun2)
gevent.joinall([g1, g2]) #等待子线程结束
爬虫示例代码
具体的示例
from gevent import monkey
monkey.patch_all()
import time
import gevent
import requests
usl_list = [
'http://www.baidu.com',
'http://www.4399.com',
'http://www.7k7k.com',
'http://www.sogou.com',
'http://www.sohu.com',
'http://www.sina.com',
'http://www.jd.com',
'https://www.luffycity.com/home',
'https://www.douban.com',
'http://www.cnblogs.com/Eva-J/articles/8324673.html',
'http://www.baidu.com',
'http://www.7k7k.com',
'http://www.sogou.com',
'http://www.sohu.com',
'https://www.luffycity.com/home',
]
def get_url(url):
response = requests.get(url)
if response.status_code == 200: #查看验证网页是否通
print(url, len(response.text)) #response.text网页的信息
pass
start = time.time()
for url in usl_list:
get_url(url)
print(time.time() - start) #7.55
start = time.time()
g_lis = []
for url in usl_list:
g = gevent.spawn(get_url, url)
#响应网页有延迟, 在延迟期间切换其他任务执行
g_lis.append(g)
gevent.joinall(g_lis)
print(time.time() - start) # 1.7