【问题标题】:Python Multi Threading with Blocking I/O具有阻塞 I/O 的 Python 多线程
【发布时间】:2016-07-11 13:13:32
【问题描述】:

我的应用程序使用了多个需要一段时间才能完成的 I/O 阻塞(网络)请求。 我尝试使用多线程,但它似乎没有带来任何加速,我猜这与 Python 的 GIL 有关。

问题是所有请求都可以同时完成,并且彼此之间没有依赖关系。我该如何解决这个性能问题?

我的代码

import threading
import urllib2
import time
def send_request(url, count_str):
    start_time = time.time()
    urllib2.urlopen(url)
    print "Request " + count_str + " took " + str(time.time() - start_time) + " started at " + str(start_time)

count = 0
for url in open('urllist.txt'):
    t = threading.Thread(target=send_request, args = (url.strip(), str(count)))
    t.start()
    count+=1

输出是

Request 1 took 5.0150949955 started at 1458789266.78
Request 2 took 10.0112490654 started at 1458789266.79
Request 0 took 15.024559021 started at 1458789266.78
Request 3 took 20.016972065 started at 1458789266.79

urllist.txt 中的 url 指向我在本地运行的服务器,需要 5 秒才能响应。 如您所见,它们都同时“开始”,但它们是阻塞的。

【问题讨论】:

  • 您可以向我们展示您的代码 :)
  • @Signal 更新了它 =) 我对 Python 中的多线程非常陌生,所以它可能有很多错误
  • q.join() 应该如何返回?您的代码中没有任何内容处理Queue 以调用.get(),更不用说.task_done()。你也没有从get_and_read_url 返回任何东西,所以你只是在排队None。最后,我要注意:对于一般的线程,特别是Queues,Python 2 bad;如果你可以迁移到更新的 Python (3.2 or later),重写的 GIL 意味着虽然线程在 CPU 绑定任务上仍然没有任何好处,但它的运行速度并没有明显变慢,并且只消耗 GIL 开销。
  • 简化代码不使用Queue,因为这个例子不需要它+我他们是你指出的错误。我添加了一些代码来衡量每个请求的开始和结束以及输出,并使问题更清晰。

标签: python multithreading performance io gil


【解决方案1】:

我无法重现您的问题(在针对少数互联网服务器进行测试时,每个服务器都重复了几次,所有请求都在大约同一时间得到服务,没有稳定增加的延迟),但是您的新输出指向一个完全不同的问题:我怀疑您使用的“本地服务器”可能不是多线程的(或者无法同时处理多个请求)。

您自己的输出表明线程是并行启动的,但请求是串行处理的;如果是 GIL 切换导致问题,我希望看到所有这些都会延迟一点(一个线程会完成一些工作,然后另一个会做更多工作,等等),而不是每个线程在下一次开始之前运行完成.这有点服务器端的问题,服务器在为其他连接提供服务之前正在处理请求以完成。

尝试进行心理调试,您是否有机会通过在服务器代码中添加休眠来实现五秒的请求时间,可能在accept 返回之后,但在启动线程来服务它之前?或者根本不在服务器上使用线程?

【讨论】:

  • 哇,原来如此。我运行的本地服务器没有启用多线程,并且在响应中阻塞。我应该关闭问题还是至少重命名它,因为它不是 Python 问题,实际上是测试性能的问题?
  • @tt_Gantz:我不会关闭它,因为实际上,在当前状态下,出于同样的原因,它提出了一个将来有人可能会合法提出的问题,它(现在)给出了足够的信息来帮助他们确定自己的问题。
【解决方案2】:

Python 线程很慢! Python 有一个 GIL(全局解释器锁),它使用互斥锁来序列化对内部的访问。您可能想看看没有 GIL 并且可以充分利用多处理器系统的 Jython。

【讨论】:

  • OP 知道 GIL 在问题中提到了它;线程应该是 I/O 绑定的,并且通常线程来协调阻塞在 I/O 上的并行任务是可以的。 “GIL 很糟糕,所以永远不要使用线程”过于简单化了,并没有增加任何内容。考虑到对 Jython 的支持有多么糟糕(比语言规范晚了五年多),这不是我的第一个建议。
猜你喜欢
  • 1970-01-01
  • 2012-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-07
相关资源
最近更新 更多