8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

python 多线程比串行慢吗?

David Harris 1月前

54 0

我正在尝试弄清楚 Python 中的多线程编程。这是我想比较串行和并行速度的简单任务。导入线程导入队列导入时间导入 ma...

我正在尝试弄清楚 Python 中的多线程编程。这是我想比较串行和并行速度的简单任务。

import threading
import Queue
import time
import math

def sinFunc(offset, n):
  result = []
  for i in range(n):
    result.append(math.sin(offset + i * i))
  return result

def timeSerial(k, n):
  t1 = time.time()    
  answers = []
  for i in range(k):
    answers.append(sinFunc(i, n))
  t2 = time.time()
  print "Serial time elapsed: %f" % (t2-t1)

class Worker(threading.Thread):

  def __init__(self, queue, name):
    self.__queue = queue
    threading.Thread.__init__(self)
    self.name = name

  def process(self, item):
    offset, n = item
    self.__queue.put(sinFunc(offset, n))
    self.__queue.task_done()
    self.__queue.task_done()

  def run(self):
    while 1:
        item = self.__queue.get()
        if item is None:
            self.__queue.task_done()
            break
        self.process(item)

def timeParallel(k, n, numThreads):
  t1 = time.time()    
  queue = Queue.Queue(0)
  for i in range(k):
    queue.put((i, n))
  for i in range(numThreads):
    queue.put(None)    
  for i in range(numThreads):
    Worker(queue, i).start()
  queue.join()
  t2 = time.time()
  print "Serial time elapsed: %f" % (t2-t1)

if __name__ == '__main__':

  n = 100000
  k = 100
  numThreads = 10

  timeSerial(k, n)
  timeParallel(k, n, numThreads)

#Serial time elapsed: 2.350883
#Serial time elapsed: 2.843030

有人能向我解释一下发生了什么吗?我习惯使用 C++,使用该模块的类似版本可以看到我们期望的速度提升。

帖子版权声明 1、本帖标题:python 多线程比串行慢吗?
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由David Harris在本站《list》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 用 C 编写的 Python 库可以随意获取/释放全局解释器锁 (GIL)。那些不使用 Python 对象的库可以释放 GIL,以便其他线程可以查看,但我相信数学库一直在使用 Python 对象,因此 math.sin 实际上是序列化的。由于锁定/解锁是一种开销,因此 Python 线程比进程慢是很正常的。

  • @cha0site 它不是 Python 特有的,而是 CPython 特有的。

  • 是的,存在问题。但是,这是否意味着线程“几乎总是”会造成伤害,即使是明智地使用 I/O 绑定线程?

  • 我理解 GIL 问题。我的意思是,如果线程中正在执行的工作是 IO 绑定的,那么它将适合线程化方法,因为 GIL 可以经常释放。我指的是它是一个笼统的 Python 线程语句。

  • @jdi:这不是线程的某些固有属性,而是 Python 特有的。在 Python 中,您有 GIL,这是一个整体锁,您需要在接触任何 Python 对象之前获取它。因此,Python 中几乎所有线程的使用都会被序列化,忽略任何性能优势,但仍然需要线程的开销。我链接到的演讲展示了线程由于 GIL 而减慢 Python 速度的一些其他方式。有些极端情况下线程可以工作,但我所知道的所有这些都涉及外部 C 模块执行奇特的操作。

  • 您认为“几乎总是无法使其更快”的说法真的正确吗?这难道不完全取决于应用程序是否受 IO 或 CPU 限制吗?我觉得笼统的说法具有误导性。

  • Python 存在严重的线程问题。基本上,向 Python 应用程序添加线程几乎总是无法使其速度更快,有时甚至会使速度更慢。

    这是由于 全局解释器锁 (GIL)造成的。

    这是 blog post ,其中包括有关该主题的讨论。

    绕过此限制的一种方法是使用进程而不是线程; multiprocessing 模块使这变得更容易。

  • 其他答案提到了 GIL 是 cpython 中的问题。但我觉得缺少了一些信息。当您在线程中运行的代码受 CPU 限制时,这会导致性能问题。就您这里的情况而言,在线程中进行许多计算很可能会导致性能急剧下降。

    但是,如果你正在做一些更依赖 IO 的事情,比如从网络应用程序中的多个套接字读取数据,或者调用子进程,那么你就可以通过线程来提高性能。上面代码的一个简单示例是向 shell 添加一个非常简单的调用:

    import os
    
    def sinFunc(offset, n):
      result = []
      for i in xrange(n):
        result.append(math.sin(offset + i * i))
      os.system("echo 'could be a database query' >> /dev/null; sleep .1")
      return result
    

    该调用可能确实存在,例如等待文件系统。但您可以看到,在此示例中,线程将开始证明其有益,因为当线程等待 IO 时可以释放 GIL,其他线程将继续处理。即便如此,当更多线程开始因创建和同步它们的开销而变得无用时,仍然存在一个最佳点。

    对于 CPU 密集型代码,你可以使用 multiprocessing

    摘自文章: http://www.informit.com/articles/article.aspx?p=1850445&seqNum=9

    ...线程更适合 I/O 密集型应用程序(I/O 释放 GIL,允许更多并发)...

    关于线程与进程的类似问题参考:
    https://.com/a/1227204/496445
    https://.com/a/990436/496445

  • 引用 10

    也许吧,但这不是这里的问题。OP 一头扎进了 GIL,这个问题通常可以通过以下方式解决:

  • 线程并不是添加到应用程序中的神奇设备,它不会变得更快。请注意,制作线程的成本是

返回
作者最近主题: