首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python 学习手册
Python Cookbook
Python 基础教程
Python Sites
PyPI - Python Package Index
http://www.simple-is-better.com/
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
V2EX  ›  Python

关于 asyncio 创建多个 tcp 连接,线程数不准确的问题

  •  
  •   1462326016 · 4 天前 · 926 次点击

    按理说 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 个连接。

    第 1 条附言  ·  4 天前

    已经找到了线程的来源,感谢大家的回复。 通过查看源码,是由于在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函数达到异步的目的。

    8 回复  |  直到 2019-08-14 14:17:19 +08:00
        1
    BBCCBB   4 天前
    你该用 currentThread(0
        2
    1462326016   4 天前
    @BBCCBB 可是我要获取的是当前线程总数量,为什么要用 currentThread 呢?
        3
    BBCCBB   4 天前
    当前 python 开的所有线程里并不是全部用来跑整个 eventloop

    还有其他的用途, 比如还需要 gc 线程等.

    照你问题里说的, 你是要看 eventloop 里多个 tcp 的处理是否是同一个线程, 不用 currentThread 用啥???
        4
    1462326016   4 天前
    @BBCCBB 感谢回复,我在获取线程数量下方添加了 currentThread 函数,获取到的对象都是同一个 Thread 对象,证实了当前的十个连接都是在同一个线程上的。但是为什么单单在打开 tcp 连接的时候出现这么多线程呢?如果不打开 tcp 连接,只是把 task 函数 sleep 模拟下任务的话就只有一个主线程是活动的,线程数量总是 1。
        5
    cs010   4 天前 via Android   ♥ 5
    asyncio 是多路复用毫无问题,为什么会出现很多线程,是因为你这里的 baidu.com 。asyncio 是扔给线程池做 DNS 解析达到异步效果的,看源码可证实。你把 baidu.com 换成 IP 即可看到效果
        6
    1462326016   4 天前
    @cs010 非常感谢, 回答正确, 我试了下的确就是我想要的结果,感谢感谢!我去翻翻源码看看怎么实现的。
        7
    gaokevin163   4 天前
    就算没有其他的解析 DNS 或者其他的一些任务 就单单处理 io 的线程也可以有多个,只要每个线程处理的链接不止一个就是多路复用
        8
    1462326016   4 天前
    @gaokevin163 恩恩,这个我是了解的,可以通过自己新开线程然后在新的线程中新建事件循环达到多个线程处理 io 的目的。发帖子的目的主要就是弄清楚其他线程是干什么用的。
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2240 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 17ms · UTC 12:53 · PVG 20:53 · LAX 05:53 · JFK 08:53
    ♥ Do have faith in what you're doing.