如何在Python中使用线程?

我试图理解Python中的线程。我已经看过文档和示例,但坦率地说,许多示例过于复杂,我无法理解它们。

如何清楚地显示为多线程划分的任务?


答案 1

自从2010年提出这个问题以来,关于如何用Python进行简单的多线程处理,以及地图,已经有了真正的简化。

下面的代码来自一篇文章/博客文章,你绝对应该检查出来(没有隶属关系) - 一行并行性:日常线程任务的更好模型。我将总结如下 - 它最终只有几行代码:

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


实现

映射函数的并行版本由两个库提供:multiprocessing,以及它鲜为人知但同样出色的步骤child:multiprocessing.dummy。

multiprocessing.dummy与多处理模块完全相同,但使用线程代替(一个重要的区别 - 对 CPU 密集型任务使用多个进程;用于(和期间)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的有用评论。


答案 2

下面是一个简单的示例:您需要尝试几个备用 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解析和响应,将其内容放在队列上;每个线程都是一个守护进程(如果主线程结束,则不会保持进程的运行 - 这比不常见);主线程启动所有子线程,在队列上执行 a,直到其中一个子线程完成 ,然后发出结果并终止(这会删除可能仍在运行的任何子线程,因为它们是守护进程线程)。getput

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


推荐