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

如何在 Python 中使用线程?

Cadoiz 1月前

95 0

我想要一个清晰的例子来展示任务是如何分布在多个线程中的。

我想要一个清晰的例子来展示任务是如何分布在多个线程中的。

帖子版权声明 1、本帖标题:如何在 Python 中使用线程?
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Cadoiz在本站《pandas》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 记住用 C 编写你的核心逻辑并通过 ctypes 调用它以真正充分利用 Python 线程。

  • 如果出于性能原因(例如数值计算)确实需要线程,请用 C++ 编写瓶颈代码,并使用 pybind11 使其成为 pymodule。我不明白为什么人们会因此说 Python 会消亡。

  • map 进行简单的多线程处理已经有了真正的简化 pool .

    下面的代码来自一篇文章/博客文章,你一定要看看(无关联) - 一行中的并行性:日常线程任务的更好模型 。我将在下面总结 - 它最终只是几行代码:

    from multiprocessing.dummy import Pool as ThreadPool
    pool = ThreadPool(4)
    results = pool.map(my_function, my_array)
    

    这是多线程版本:

    results = []
    for item in my_array:
        results.append(my_function(item))
    

    描述

    Map 是一个很酷的小函数,也是轻松将并行性注入 Python 代码的关键。对于那些不熟悉的人来说,map 是从 Lisp 等函数式语言中借鉴而来的东西。它是一个将另一个函数映射到序列上的函数。

    Map 为我们处理序列上的迭代,应用函数,并在最后将所有结果存储在一个方便的列表中。

    Enter image description here


    执行

    两个库提供了 map 函数的并行版本:multiprocessing,以及其鲜为人知但同样出色的子步骤:multiprocessing.dummy。

    multiprocessing.dummy 与多处理模块完全相同, 但是使用线程 an important distinction - use multiple processes for CPU-intensive tasks; threads for (and during) I/O ):

    multiprocessing.dummy 复制了多处理的 API,但只不过是线程模块的包装器。

    import urllib2
    from multiprocessing.dummy import Pool as ThreadPool
    
    urls = [
      'http://www.python.org',
      'http://www.python.org/about/',
      'http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html',
      'http://www.python.org/doc/',
      'http://www.python.org/download/',
      'http://www.python.org/getit/',
      'http://www.python.org/community/',
      'https://wiki.python.org/moin/',
    ]
    
    # Make the Pool of workers
    pool = ThreadPool(4)
    
    # Open the URLs in their own threads
    # and return the results
    results = pool.map(urllib2.urlopen, urls)
    
    # Close the pool and wait for the work to finish
    pool.close()
    pool.join()
    

    计时结果如下:

    Single thread:   14.4 seconds
           4 Pool:   3.1 seconds
           8 Pool:   1.4 seconds
          13 Pool:   1.3 seconds
    

    传递多个参数 仅在 Python 3.3 及更高版本中 有效 ):

    传递多个数组:

    results = pool.starmap(function, zip(list_a, list_b))
    

    或者传递一个常量和一个数组:

    results = pool.starmap(function, zip(itertools.repeat(constant), list_a))
    

    如果您使用的是早期版本的 Python,则可以通过 此解决方法 )。

    (感谢 user136036 有益评论。)

  • 这只是因为发布时间太新,所以缺少投票。这个答案非常出色,并演示了“地图”功能,它提供了比此处其他答案更容易理解的语法。

  • 顺便说一句,伙计们,你可以将 with Pool(8) 写为 p: p.map( *whatever* ),这样就可以摆脱簿记行了。

  • @BarafuAlbino:虽然这很有用,但值得注意的是,它只在 Python 3.3+ 中有效。

  • 您怎么能留下这个答案而不提及这只对 I/O 操作有用呢?这只在单个线程上运行,在大多数情况下是无用的,而且实际上比以正常方式执行要慢。

  • 这是一个简单的例子:您需要尝试几个备选的 URL,并返回第一个响应的 URL 的内容。

    import Queue
    import threading
    import urllib2
    
    # Called by each thread
    def get_url(q, url):
        q.put(urllib2.urlopen(url).read())
    
    theurls = ["http://google.com", "http://yahoo.com"]
    
    q = Queue.Queue()
    
    for u in theurls:
        t = threading.Thread(target=get_url, args = (q,u))
        t.daemon = True
        t.start()
    
    s = q.get()
    print s
    

    这是使用线程进行简单优化的情况:每个子线程都在等待 URL 解析和响应,以将其内容放入队列;每个线程都是守护进程(如果主线程结束,则不会保持进程运行 - 这很常见);主线程启动所有子线程,对 get 队列执行 ,直到其中一个子线程执行 put ,然后发出结果并终止(这会关闭可能仍在运行的任何子线程,因为它们是守护线程)。

    在 Python 中正确使用线程总是与 I/O 操作相关(因为 CPython 无论如何都不会使用多个核心来运行 CPU 密集型任务,所以线程的唯一原因是在等待某些 I/O 时不会阻塞进程)。顺便说一句,队列几乎总是将工作分配给线程和/或收集工作结果的最佳方式,而且它们本质上是线程安全的,因此它们可以让您免于担心锁、条件、事件、信号量和其他线程间协调/通信概念。

  • 再次感谢 MartelliBot。我已更新示例以等待所有 URL 响应: import Queue、threading、urllib2 q = Queue.Queue() urls = '''a.com b.com c.com'''.split() urls_received = 0 def get_url(q, url): req = urllib2.Request(url) resp = urllib2.urlopen(req) q.put(resp.read()) global urls_received urls_received +=1 print urls_received for u in urls: t = threading.Thread(target=get_url, args = (q,u)) t.daemon = True t.start() while q.empty() and urls_received < len(urls): s = q.get() print s

  • @JRM:如果你看下面的下一个答案,我认为等待线程完成的更好方法是使用 join() 方法,因为这会使主线程等待它们完成,而不会通过不断检查值来消耗处理器。@Alex:谢谢,这正是我理解如何使用线程所需要的。

  • 对于 python3,将“import urllib2”替换为“import urllib.request as urllib2”。并在打印语句中加上括号。

  • 对于 Python 3,将 Queue 模块名称替换为queue。方法名称相同。

  • 我注意到解决方案只会打印出其中一页。要从队列中打印两页,只需再次运行命令:s = q.get() print s @krs013 您不需要连接,因为 Queue.get() 正在阻塞。

  • 注意 :对于 Python 中的实际并行化,您应该使用 multiprocessing 模块来分叉并行执行的多个进程(由于全局解释器锁,Python 线程提供交错,但它们实际上是串行执行的,而不是并行执行的,并且仅在交错 I/O 操作时有用)。

    但是,如果您只是在寻找交错(或者正在执行可以并行化的 I/O 操作,尽管存在全局解释器锁),那么 threading 模块就是开始的地方。作为一个非常简单的例子,让我们考虑通过并行求和子范围来求和大范围的问题:

    import threading
    
    class SummingThread(threading.Thread):
         def __init__(self,low,high):
             super(SummingThread, self).__init__()
             self.low=low
             self.high=high
             self.total=0
    
         def run(self):
             for i in range(self.low,self.high):
                 self.total+=i
    
    
    thread1 = SummingThread(0,500000)
    thread2 = SummingThread(500000,1000000)
    thread1.start() # This actually causes the thread to run
    thread2.start()
    thread1.join()  # This waits until the thread has completed
    thread2.join()
    # At this point, both threads have completed
    result = thread1.total + thread2.total
    print result
    

    请注意,上面是一个非常愚蠢的例子,因为它完全不执行任何 I/O,并且 CPython CPython

  • Gili 1月前 0 只看Ta
    引用 16

    @Alex,我并不是说它很实用,但它确实演示了如何定义和产生线程,我认为这是 OP 想要的。

  • 虽然这确实显示了如何定义和产生线程,但实际上它并没有并行地对子范围求和。线程 1 运行直到完成,而主线程阻塞,然后线程 2 发生同样的事情,然后主线程恢复并打印出它们累积的值。

  • 那不应该是 super(SummingThread, self).__init__() 吗?如 .com/a/2197625/806988

  • 引用 19

    @JamesAndres,假设没有人从 \'SummingThread\' 继承,那么任何一个都可以正常工作;在这种情况下,super(SummingThread, self) 只是一种按方法解析顺序 (MRO) 查找下一个类的奇特方法,即 threading.Thread(然后在两种情况下都调用它的 init)。不过,您说得对,对于当前的 Python 来说,使用 super() 是更好的风格。在我提供这个答案时,Super 相对较新,因此直接调用超类而不是使用 super()。不过,我会更新它以使用 super。

  • 警告:不要在这样的任务中使用多线程!正如 Dave Beazley 所展示的:dabeaz.com/python/NewGIL.pdf,2 个 CPU 上的 2 个 Python 线程执行 CPU 密集型任务的速度比 1 个 CPU 上的 1 个线程慢 2 倍,比 1 个 CPU 上的 2 个线程慢 1.5 倍。这种奇怪的行为是由于 OS 和 Python 之间的努力协调不当造成的。线程的实际用例是 I/O 密集型任务。例如,当您通过网络执行读/写操作时,将等待数据读取/写入的线程置于后台并将 CPU 切换到需要处理数据的另一个线程是有意义的。

返回
作者最近主题: