翻译12.1节

This commit is contained in:
Tony Yang
2015-08-20 08:39:20 +08:00
parent 62d71cb6ad
commit c0be80f293

View File

@@ -5,148 +5,134 @@
----------
问题
----------
You want to create and destroy threads for concurrent execution of code.
|
你要为需要并发执行的代码创建/销毁线程
----------
解决方案
----------
The threading library can be used to execute any Python callable in its own thread. To
do this, you create a Thread instance and supply the callable that you wish to execute
as a target. Here is a simple example:
``threading`` 库可以在单独的线程中执行任何的在 Python 中可以调用的对象。你可以创建一个 ``Thread`` 对象并将你要执行的对象以 target 参数的形式提供给该对象。
下面是一个简单的例子:
# Code to execute in an independent thread
import time
def countdown(n):
while n > 0:
print('T-minus', n)
n -= 1
time.sleep(5)
.. code-block:: python
# Create and launch a thread
from threading import Thread
t = Thread(target=countdown, args=(10,))
t.start()
# Code to execute in an independent thread
import time
def countdown(n):
while n > 0:
print('T-minus', n)
n -= 1
time.sleep(5)
When you create a thread instance, it doesnt start executing until you invoke its start()
method (which invokes the target function with the arguments you supplied).
Threads are executed in their own system-level thread (e.g., a POSIX thread or Windows
threads) that is fully managed by the host operating system. Once started, threads run
independently until the target function returns. You can query a thread instance to see
if its still running:
# Create and launch a thread
from threading import Thread
t = Thread(target=countdown, args=(10,))
t.start()
if t.is_alive():
print('Still running')
else:
print('Completed')
当你创建好一个线程对象后,该对象并不会立即执行,除非你调用它的 ``start()`` 方法(当你调用 ``start()`` 方法时它会调用你传递进来的函数并把你传递进来的参数传递给该函数。Python中的线程会在一个单独的系统级线程中执行比如说一个 POSIX 线程或者一个 Windows 线程),这些线程将由操作系统来全权管理。线程一旦启动,将独立执行直到目标函数返回。你可以查询一个线程对象的状态,看它是否还在执行:
You can also request to join with a thread, which waits for it to terminate:
.. code-block:: python
if t.is_alive():
print('Still running')
else:
print('Completed')
t.join()
你也可以将一个线程加入到当前线程,并等待它终止:
The interpreter remains running until all threads terminate. For long-running threads
or background tasks that run forever, you should consider making the thread daemonic.
For example:
.. code-block:: python
t = Thread(target=countdown, args=(10,), daemon=True)
t.start()
t.join()
Daemonic threads cant be joined. However, they are destroyed automatically when the
main thread terminates.
Beyond the two operations shown, there arent many other things you can do with
threads. For example, there are no operations to terminate a thread, signal a thread,
adjust its scheduling, or perform any other high-level operations. If you want these
features, you need to build them yourself.
If you want to be able to terminate threads, the thread must be programmed to poll for
exit at selected points. For example, you might put your thread in a class such as this:
Python解释器在所有线程都终止后才继续执行代码剩余的部分。对于需要长时间运行的线程或者需要一直运行的后台任务你应当考虑使用后台线程。
例如:
class CountdownTask:
def __init__(self):
self._running = True
.. code-block:: python
t = Thread(target=countdown, args=(10,), daemon=True)
t.start()
def terminate(self):
self._running = False
后台线程无法等待,不过,这些线程会在主线程终止时自动销毁。
除了如上所示的两个操作,并没有太多可以对线程做的事情。你无法结束一个线程,无法给它发送信号,无法调整它的调度,也无法执行其他高级操作。如果需要这些特性,你需要自己添加。比如说,如果你需要终止线程,那么这个线程必须通过编程在某个特定点轮询来退出。你可以像下边这样把线程放入一个类中:
def run(self, n):
while self._running and n > 0:
print('T-minus', n)
n -= 1
time.sleep(5)
.. code-block:: python
c = CountdownTask()
t = Thread(target=c.run, args=(10,))
t.start()
...
c.terminate() # Signal termination
t.join() # Wait for actual termination (if needed)
class CountdownTask:
def __init__(self):
self._running = True
Polling for thread termination can be tricky to coordinate if threads perform blocking
operations such as I/O. For example, a thread blocked indefinitely on an I/O operation
may never return to check if its been killed. To correctly deal with this case, youll need
to carefully program thread to utilize timeout loops. For example:
def terminate(self):
self._running = False
class IOTask:
def terminate(self):
self._running = False
def run(self, n):
while self._running and n > 0:
print('T-minus', n)
n -= 1
time.sleep(5)
def run(self, sock):
# sock is a socket
sock.settimeout(5) # Set timeout period
while self._running:
# Perform a blocking I/O operation w/ timeout
try:
data = sock.recv(8192)
break
except socket.timeout:
continue
# Continued processing
...
# Terminated
return
c = CountdownTask()
t = Thread(target=c.run, args=(10,))
t.start()
c.terminate() # Signal termination
t.join() # Wait for actual termination (if needed)
如果线程执行一些像I/O这样的阻塞操作那么通过轮询来终止线程将使得线程之间的协调变得非常棘手。比如如果一个线程一直阻塞在一个I/O操作上它就永远无法返回也就无法检查自己是否已经被结束了。要正确处理这些问题你需要利用超时循环来小心操作线程。
例子如下:
.. code-block:: python
class IOTask:
def terminate(self):
self._running = False
def run(self, sock):
# sock is a socket
sock.settimeout(5) # Set timeout period
while self._running:
# Perform a blocking I/O operation w/ timeout
try:
data = sock.recv(8192)
break
except socket.timeout:
continue
# Continued processing
...
# Terminated
return
|
----------
讨论
----------
Due to a global interpreter lock (GIL), Python threads are restricted to an execution
model that only allows one thread to execute in the interpreter at any given time. For
this reason, Python threads should generally not be used for computationally intensive
tasks where you are trying to achieve parallelism on multiple CPUs. They are much
better suited for I/O handling and handling concurrent execution in code that performs
blocking operations (e.g., waiting for I/O, waiting for results from a database, etc.).
Sometimes you will see threads defined via inheritance from the Thread class. For
example:
由于全局解释锁GIL的原因Python 的线程被限制到同一时刻只允许一个线程执行这样一个执行模型。所以Python 的线程更适用于处理I/O和其他需要并发执行的阻塞操作比如等待I/O、等待从数据库获取数据等等而不是需要多处理器并行的计算密集型任务。
from threading import Thread
有时你会看到下边这种通过继承 ``Thread`` 类来实现的线程:
class CountdownThread(Thread):
def __init__(self, n):
super().__init__()
self.n = 0
def run(self):
while self.n > 0:
.. code-block:: python
from threading import Thread
print('T-minus', self.n)
self.n -= 1
time.sleep(5)
class CountdownThread(Thread):
def __init__(self, n):
super().__init__()
self.n = 0
def run(self):
while self.n > 0:
c = CountdownThread(5)
c.start()
print('T-minus', self.n)
self.n -= 1
time.sleep(5)
Although this works, it introduces an extra dependency between the code and the
threading library. That is, you can only use the resulting code in the context of threads,
whereas the technique shown earlier involves writing code with no explicit dependency
on threading. By freeing your code of such dependencies, it becomes usable in other
contexts that may or may not involve threads. For instance, you might be able to execute
your code in a separate process using the multiprocessing module using code like this:
c = CountdownThread(5)
c.start()
import multiprocessing
c = CountdownTask(5)
p = multiprocessing.Process(target=c.run)
p.start()
...
尽管这样也可以工作,但这使得你的代码依赖于 ``threading`` 库,所以你的这些代码只能在线程上下文中使用。上文所写的那些代码、函数都是与 ``threading`` 库无关的,这样就使得这些代码可以被用在其他的上下文中,可能与线程有关,也可能与线程无关。比如,你可以通过 ``multiprocessing`` 模块在一个单独的进程中执行你的代码:
Again, this only works if the CountdownTask class has been written in a manner that is
neutral to the actual means of concurrency (threads, processes, etc.).
.. code-block:: python
import multiprocessing
c = CountdownTask(5)
p = multiprocessing.Process(target=c.run)
p.start()
再次重申,这段代码仅适用于 CountdownTask 类是以独立于实际的并发手段(多线程、多进程等等)实现的情况。