python 3.x asyncio与ThreadPoolExecutor结合使用,完成阻塞IO的请求

asyncio与ThreadPoolExecutor结合使用,完成阻塞IO的请求

  • 协程模式是单线程的,为什么又多出一个多线程呢???
    • asyncio是异步IO编程的解决方案,异步IO是包含多线程,多进程以及协程.  
    • 所以asyncio即可完成多线程(多进程)也可以完成协程.  
    • 所以在协程里面也可以使用多线程.  
  • 什么时候在协程中使用多线程呢???
    • 协程里面是不能加入阻塞IO的,但是某些时候(比如说某一个库或者说某一个接口,只能提供阻塞怎么办?实际很简单把它放在一个线程中去做即可)  
    • 在协程中集成阻塞IO,比如说MySQL库是阻塞的,PyMySQL和MySQLClient都是阻塞的,那在协程中如果去强行使用MySQL这三个库怎么办?就可以使用多线程  
  • 在asyncio中集成线程池
    • run_in_executor(self, executor, func, *args):将某个阻塞IO的函数放到executor(ThreadPoolExecutor)中运行.  
      • executor: 线程池执行者    
      • func: 函数名    
      • *args: 函数的参数    

run_in_executor(self, executor, func, *args)源码

 1        def run_in_executor(self, executor, func, *args):
 2             self._check_closed()
 3             if self._debug:
 4                 self._check_callback(func, 'run_in_executor')
 5             if executor is None:
 6                 executor = self._default_executor
 7 
 8                 # 1.如果executor为None的话,就会声明一个ThreadPoolExecutor()
 9                 if executor is None:
10                     executor = concurrent.futures.ThreadPoolExecutor()
11 
12                     # 2.将executor赋值给_default_executor,所以说将executor放到self里面(实例变量)也就是loop里面,作为一个变量
13                     self._default_executor = executor
14 
15             # wrap_future()方法将executor.submit()执行后返回的future对象,封装成新的futures(也就是协程的futures)
16             return futures.wrap_future(executor.submit(func, *args), loop=self)

 


submit(self, fn, *args, **kwargs)源码

 1 def submit(self, fn, *args, **kwargs):
 2             with self._shutdown_lock:
 3                 if self._shutdown:
 4                     raise RuntimeError('cannot schedule new futures after shutdown')
 5 
 6                 f = _base.Future()
 7                 w = _WorkItem(f, fn, args, kwargs)
 8 
 9                 self._work_queue.put(w)
10                 self._adjust_thread_count()
11 
12                 # 返回future对象.
13                 return f

 

 

 1 import asyncio
 2 from concurrent.futures import ThreadPoolExecutor
 3 import socket
 4 # urlparse的作用是对url进行解析.
 5 from urllib.parse import urlparse
 6 import time
 7 
 8 
 9 # get_url是一个阻塞的接口.将阻塞接口与asyncio结合起来.
10 def get_url(url):
11     new_url = urlparse(url)
12     host = new_url.netloc
13     path = new_url.path
14     if path == "":
15         path = "/"
16 
17     client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
18     client.connect((host, 80))  # 阻塞不会消耗cpu
19     client.send("GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format(path, host).encode("utf8"))
20 
21     data = b""
22     while True:
23         re_data = client.recv(1024)
24         if re_data:
25             data += re_data
26         else:
27             break
28 
29     data = data.decode("utf8")
30     html_data = data.split("\r\n\r\n")[1]
31     print(html_data)
32     client.close()
33 
34 
35 if __name__ == "__main__":
36     start_time = time.time()
37     loop = asyncio.get_event_loop()
38     executor = ThreadPoolExecutor(3)
39     # 要在asyncio中等待task的执行完成就要使用run_until_complete()方法,而这个方法接收一个list参数,所以需要声明一个list
40     tasks = []
41     # 为了测试asyncio与ThreadPoolExecutor结合的性能,一次性放20个函数进来.
42     for i in range(1, 21):
43         # 获取某一个商品的详情的url,随机获取前20个商品详情的url
44         url = "http://shop.projectsedu.com/goods/{}/".format(i)
45         # 这个提交过程是快速返回的.是asyncio的task
46         # 通过run_in_executor()方法的源码得知,将线程的future执行了一个包装,包装成协程的future,
47         # 这样的话,就可以进行await,所以返回一个task.
48         # run_in_executor()方法运行原理还是在内部去声明一个线程池,然后这里面的阻塞代码放到线程池里面去运行.
49         # 与线程池是一样的没有多高的性能.
50         task = loop.run_in_executor(executor, get_url, url)
51         tasks.append(task)
52     # 很简单的将阻塞的url放到loop中,同时也不会影响协程的运行.
53     loop.run_until_complete(asyncio.gather(*tasks))
54 
55     print("code run finished use time: {}".format(time.time() - start_time))
56 
57 """
58 输出结果:
59     code run finished use time: 8.041460037231445
60     
61 通过运行的时间得出结论,这种性能无非还是将一些代码放到线程池中运行.避免不了线程池的一些问题.
62 在使用协程模式的时候,在协程里面不要去调用阻塞方式.但是一定要在协程中调用阻塞方式,就可以把阻塞方式拿到线程池中运行.
63 这就是即将要说明的如何将线程池与asyncio相结合.来模拟http请求.
64 """

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

********

posted on 2019-05-22 09:22  jaydenjune  阅读(1457)  评论(0)    收藏  举报

导航