
按理说 asyncio 封装了 IO 多路复用,应该是用一个线程通过监听文件描述符的方式来管理多个 tcp 连接,但实际测试中好像为每个 tcp 连接创建了一个线程,但是线程总数有封顶,40 个,这个不是特别理解,为什么不是一个线程对应多个 tcp 连接? 测试代码如下:
# -*- coding: utf-8 -*- import asyncio import threading loop = asyncio.get_event_loop() async def task(): print('start11') print('当前线程总数:' + threading.activeCount().__str__()) await asyncio.open_connection('www.baidu.com', 80) await asyncio.sleep(10) print('stop11') async def task2(): print('start22') print('全部启动之后线程数:' + threading.activeCount().__str__()) await asyncio.sleep(13) print('stop22') for a in range(10): loop.create_task(task()) print(f'协程任务总数为:{asyncio.Task.all_tasks().__len__()}') loop.run_until_complete(task2()) 输出为:
协程任务总数为:10 start11 当前线程总数:1 start11 当前线程总数:2 start11 当前线程总数:3 start11 当前线程总数:4 start11 当前线程总数:5 start11 当前线程总数:6 start11 当前线程总数:7 start11 当前线程总数:8 start11 当前线程总数:9 start11 当前线程总数:10 start22 全部启动之后线程数:11 如果把 for 循环中任务数改为 100,最后总共会创建 41 个线程,100 个连接。
已经找到了线程的来源,感谢大家的回复。 通过查看源码,是由于在getaddrinfo时创建了线程池造成的。具体源码位置为:
def run_in_executor(self, executor, func, *args): self._check_closed() if self._debug: self._check_callback(func, 'run_in_executor') if executor is None: executor = self._default_executor if executor is None: executor = concurrent.futures.ThreadPoolExecutor() self._default_executor = executor return futures.wrap_future( executor.submit(func, *args), loop=self) 通过线程池去执行socket.getaddrinfo函数达到异步的目的。
1 BBCCBB 2019-08-14 08:44:04 +08:00 你该用 currentThread(0 |
2 1462326016 OP @BBCCBB 可是我要获取的是当前线程总数量,为什么要用 currentThread 呢? |
3 BBCCBB 2019-08-14 09:03:28 +08:00 当前 python 开的所有线程里并不是全部用来跑整个 eventloop 还有其他的用途, 比如还需要 gc 线程等. 照你问题里说的, 你是要看 eventloop 里多个 tcp 的处理是否是同一个线程, 不用 currentThread 用啥??? |
4 1462326016 OP @BBCCBB 感谢回复,我在获取线程数量下方添加了 currentThread 函数,获取到的对象都是同一个 Thread 对象,证实了当前的十个连接都是在同一个线程上的。但是为什么单单在打开 tcp 连接的时候出现这么多线程呢?如果不打开 tcp 连接,只是把 task 函数 sleep 模拟下任务的话就只有一个主线程是活动的,线程数量总是 1。 |
5 snBDX1b0jJM4ogKd 2019-08-14 09:35:50 +08:00 via Android |
6 1462326016 OP @cs010 非常感谢, 回答正确, 我试了下的确就是我想要的结果,感谢感谢!我去翻翻源码看看怎么实现的。 |
7 gaokevin163 2019-08-14 11:54:45 +08:00 就算没有其他的解析 DNS 或者其他的一些任务 就单单处理 io 的线程也可以有多个,只要每个线程处理的链接不止一个就是多路复用 |
8 1462326016 OP @gaokevin163 恩恩,这个我是了解的,可以通过自己新开线程然后在新的线程中新建事件循环达到多个线程处理 io 的目的。发帖子的目的主要就是弄清楚其他线程是干什么用的。 |