V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
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
Achilless
V2EX  ›  Python

关于 py 的线程池,并发请求接口

  •  
  •   Achilless · 2019-10-23 14:00:37 +08:00 · 2768 次点击
    这是一个创建于 1877 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我向一个 api 接口,用 requests 访问四万次,线程池设置为 20 的时候,跑完花了 440 秒,设置为 30 的时候,花了 570 秒,设为 10 的时候最快,只用了 277 秒。 为什么这里并发越高,执行的还越慢呢?

    7 条回复    2019-10-27 19:45:59 +08:00
    ClericPy
        1
    ClericPy  
       2019-10-23 15:02:01 +08:00
    class ThreadPoolExecutor(_base.Executor):

    # Used to assign unique thread names when thread_name_prefix is not supplied.
    _counter = itertools.count().__next__

    def __init__(self, max_workers=None, thread_name_prefix='',
    initializer=None, initargs=()):
    """Initializes a new ThreadPoolExecutor instance.

    Args:
    max_workers: The maximum number of threads that can be used to
    execute the given calls.
    thread_name_prefix: An optional name prefix to give our threads.
    initializer: An callable used to initialize worker threads.
    initargs: A tuple of arguments to pass to the initializer.
    """
    if max_workers is None:
    # Use this number because ThreadPoolExecutor is often
    # used to overlap I/O instead of CPU work.
    max_workers = (os.cpu_count() or 1) * 5


    这里源码里都写了建议线程池大小了

    对 Python 来说, 多线程并不会利用多核, 所以一堆线程是靠系统的不断切换来确定线程完成状态的, 切换的多了自然开销就大了, 性能损失也就大了

    多进程也是一个道理, 你就俩核心的话, 不断切换来切换去, 那 CPU 大部分时间都在切换状态上, 根本没时间干活, 也会变慢

    权威一点的搜 Google , 一个意思

    https://www.google.com.hk/search?q=Python+best+thread+pool+size

    https://stackoverflow.com/questions/42541893/maximum-pool-size-when-using-threadpool-python
    ClericPy
        2
    ClericPy  
       2019-10-23 15:05:29 +08:00
    requests 只是让代码写起来快, 跑起来想快还是得考虑其他的, 比如用 gevent 代替 threading, 比如用 aiohttp 代替 requests, aiohttp 的 qps 不开 uvloop 的情况下是 requests + threading 的三倍多, 虽然 golang 原生 net/http 是 requests 十几倍...

    这个年代了, 协程的 CPU 利用率高, 状态切换开销小, 不用考虑并发限制(也可以手动干预限制), 很少考虑多线程竞态的锁关系, 学点也不吃亏
    Achilless
        3
    Achilless  
    OP
       2019-10-23 16:05:48 +08:00
    @ClericPy 感谢指导,话说您的意思是说用协程的话就不会出现这种切换开销大导致的效率低下么,或者说就算出现也是在更大的并发下,比如 100
    xiaolinjia
        4
    xiaolinjia  
       2019-10-23 17:08:42 +08:00
    协程其实是单线程里的轮询机制。
    ClericPy
        5
    ClericPy  
       2019-10-24 10:18:05 +08:00
    @Achilless 协程的切换是用户主动操作的精确切换, 没有太多多余开销, 而且内存也比较省, CPU 效率更高, 至于并发, 没什么压力, 可以去 Google 看看 Python coroutine 的相关文章, 内部实现比较复杂, 但是对用户的接口依然是 Future 那套, 不是太难学
    Achilless
        6
    Achilless  
    OP
       2019-10-26 21:46:20 +08:00
    @ClericPy 我第二天用 go 重写了代码,也出现了并发数高了变慢的现象,应该原因也一样。但是速度提升也不是很理想,10 个线程 150 秒,提升了算一半吧。不会 go,边 google 边写的,不知道代码有没有问题。
    ClericPy
        7
    ClericPy  
       2019-10-27 19:45:59 +08:00
    @Achilless 我居然没收到提醒有人回复我...

    这估计和 go 没什么关系, 对 goroutine 来说 10 个和 100 个差别不该这么大, 主要开销还是在服务端的样子, 服务端扛不住太高并发, 而不是本地 CPU 扛不住
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1277 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 17:28 · PVG 01:28 · LAX 09:28 · JFK 12:28
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.