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

2019-10-23 14:00:37 +08:00
 Achilless

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

2670 次点击
所在节点    Python
7 条回复
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
2019-10-23 15:05:29 +08:00
requests 只是让代码写起来快, 跑起来想快还是得考虑其他的, 比如用 gevent 代替 threading, 比如用 aiohttp 代替 requests, aiohttp 的 qps 不开 uvloop 的情况下是 requests + threading 的三倍多, 虽然 golang 原生 net/http 是 requests 十几倍...

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

这估计和 go 没什么关系, 对 goroutine 来说 10 个和 100 个差别不该这么大, 主要开销还是在服务端的样子, 服务端扛不住太高并发, 而不是本地 CPU 扛不住

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/612087

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX