|
5 | 5 | ----------
|
6 | 6 | 问题
|
7 | 7 | ----------
|
8 |
| -You want to create and destroy threads for concurrent execution of code. |
9 |
| - |
10 |
| -| |
| 8 | +你要为需要并发执行的代码创建/销毁线程 |
11 | 9 |
|
12 | 10 | ----------
|
13 | 11 | 解决方案
|
14 | 12 | ----------
|
15 |
| -The threading library can be used to execute any Python callable in its own thread. To |
16 |
| -do this, you create a Thread instance and supply the callable that you wish to execute |
17 |
| -as a target. Here is a simple example: |
18 |
| - |
19 |
| -# Code to execute in an independent thread |
20 |
| -import time |
21 |
| -def countdown(n): |
22 |
| - while n > 0: |
23 |
| - print('T-minus', n) |
24 |
| - n -= 1 |
25 |
| - time.sleep(5) |
26 |
| - |
27 |
| -# Create and launch a thread |
28 |
| -from threading import Thread |
29 |
| -t = Thread(target=countdown, args=(10,)) |
30 |
| -t.start() |
31 |
| - |
32 |
| -When you create a thread instance, it doesn’t start executing until you invoke its start() |
33 |
| -method (which invokes the target function with the arguments you supplied). |
34 |
| -Threads are executed in their own system-level thread (e.g., a POSIX thread or Windows |
35 |
| -threads) that is fully managed by the host operating system. Once started, threads run |
36 |
| -independently until the target function returns. You can query a thread instance to see |
37 |
| -if it’s still running: |
38 |
| - |
39 |
| -if t.is_alive(): |
40 |
| - print('Still running') |
41 |
| -else: |
42 |
| - print('Completed') |
43 |
| - |
44 |
| -You can also request to join with a thread, which waits for it to terminate: |
45 |
| - |
46 |
| - t.join() |
47 |
| - |
48 |
| -The interpreter remains running until all threads terminate. For long-running threads |
49 |
| -or background tasks that run forever, you should consider making the thread daemonic. |
50 |
| -For example: |
51 |
| - |
52 |
| -t = Thread(target=countdown, args=(10,), daemon=True) |
53 |
| -t.start() |
54 |
| - |
55 |
| -Daemonic threads can’t be joined. However, they are destroyed automatically when the |
56 |
| -main thread terminates. |
57 |
| -Beyond the two operations shown, there aren’t many other things you can do with |
58 |
| -threads. For example, there are no operations to terminate a thread, signal a thread, |
59 |
| -adjust its scheduling, or perform any other high-level operations. If you want these |
60 |
| -features, you need to build them yourself. |
61 |
| -If you want to be able to terminate threads, the thread must be programmed to poll for |
62 |
| -exit at selected points. For example, you might put your thread in a class such as this: |
63 |
| - |
64 |
| -class CountdownTask: |
65 |
| - def __init__(self): |
66 |
| - self._running = True |
67 |
| - |
68 |
| - def terminate(self): |
69 |
| - self._running = False |
70 |
| - |
71 |
| - def run(self, n): |
72 |
| - while self._running and n > 0: |
73 |
| - print('T-minus', n) |
74 |
| - n -= 1 |
75 |
| - time.sleep(5) |
76 |
| - |
77 |
| -c = CountdownTask() |
78 |
| -t = Thread(target=c.run, args=(10,)) |
79 |
| -t.start() |
80 |
| -... |
81 |
| -c.terminate() # Signal termination |
82 |
| -t.join() # Wait for actual termination (if needed) |
83 |
| - |
84 |
| -Polling for thread termination can be tricky to coordinate if threads perform blocking |
85 |
| -operations such as I/O. For example, a thread blocked indefinitely on an I/O operation |
86 |
| -may never return to check if it’s been killed. To correctly deal with this case, you’ll need |
87 |
| -to carefully program thread to utilize timeout loops. For example: |
88 |
| - |
89 |
| -class IOTask: |
90 |
| - def terminate(self): |
91 |
| - self._running = False |
92 |
| - |
93 |
| - def run(self, sock): |
94 |
| - # sock is a socket |
95 |
| - sock.settimeout(5) # Set timeout period |
96 |
| - while self._running: |
97 |
| - # Perform a blocking I/O operation w/ timeout |
98 |
| - try: |
99 |
| - data = sock.recv(8192) |
100 |
| - break |
101 |
| - except socket.timeout: |
102 |
| - continue |
103 |
| - # Continued processing |
104 |
| - ... |
105 |
| - # Terminated |
106 |
| - return |
107 |
| - |
| 13 | +``threading`` 库可以在单独的线程中执行任何的在 Python 中可以调用的对象。你可以创建一个 ``Thread`` 对象并将你要执行的对象以 target 参数的形式提供给该对象。 |
| 14 | +下面是一个简单的例子: |
| 15 | + |
| 16 | +.. code-block:: python |
| 17 | +
|
| 18 | + # Code to execute in an independent thread |
| 19 | + import time |
| 20 | + def countdown(n): |
| 21 | + while n > 0: |
| 22 | + print('T-minus', n) |
| 23 | + n -= 1 |
| 24 | + time.sleep(5) |
| 25 | +
|
| 26 | + # Create and launch a thread |
| 27 | + from threading import Thread |
| 28 | + t = Thread(target=countdown, args=(10,)) |
| 29 | + t.start() |
| 30 | +
|
| 31 | +当你创建好一个线程对象后,该对象并不会立即执行,除非你调用它的 ``start()`` 方法(当你调用 ``start()`` 方法时,它会调用你传递进来的函数,并把你传递进来的参数传递给该函数)。Python中的线程会在一个单独的系统级线程中执行(比如说一个 POSIX 线程或者一个 Windows 线程),这些线程将由操作系统来全权管理。线程一旦启动,将独立执行直到目标函数返回。你可以查询一个线程对象的状态,看它是否还在执行: |
| 32 | + |
| 33 | +.. code-block:: python |
| 34 | + |
| 35 | + if t.is_alive(): |
| 36 | + print('Still running') |
| 37 | + else: |
| 38 | + print('Completed') |
| 39 | +
|
| 40 | +你也可以将一个线程加入到当前线程,并等待它终止: |
| 41 | + |
| 42 | +.. code-block:: python |
| 43 | +
|
| 44 | + t.join() |
| 45 | +
|
| 46 | +Python解释器在所有线程都终止后才继续执行代码剩余的部分。对于需要长时间运行的线程或者需要一直运行的后台任务,你应当考虑使用后台线程。 |
| 47 | +例如: |
| 48 | + |
| 49 | +.. code-block:: python |
| 50 | + |
| 51 | + t = Thread(target=countdown, args=(10,), daemon=True) |
| 52 | + t.start() |
| 53 | +
|
| 54 | +后台线程无法等待,不过,这些线程会在主线程终止时自动销毁。 |
| 55 | +除了如上所示的两个操作,并没有太多可以对线程做的事情。你无法结束一个线程,无法给它发送信号,无法调整它的调度,也无法执行其他高级操作。如果需要这些特性,你需要自己添加。比如说,如果你需要终止线程,那么这个线程必须通过编程在某个特定点轮询来退出。你可以像下边这样把线程放入一个类中: |
| 56 | + |
| 57 | +.. code-block:: python |
| 58 | +
|
| 59 | + class CountdownTask: |
| 60 | + def __init__(self): |
| 61 | + self._running = True |
| 62 | +
|
| 63 | + def terminate(self): |
| 64 | + self._running = False |
| 65 | +
|
| 66 | + def run(self, n): |
| 67 | + while self._running and n > 0: |
| 68 | + print('T-minus', n) |
| 69 | + n -= 1 |
| 70 | + time.sleep(5) |
| 71 | +
|
| 72 | + c = CountdownTask() |
| 73 | + t = Thread(target=c.run, args=(10,)) |
| 74 | + t.start() |
| 75 | + c.terminate() # Signal termination |
| 76 | + t.join() # Wait for actual termination (if needed) |
| 77 | +
|
| 78 | +如果线程执行一些像I/O这样的阻塞操作,那么通过轮询来终止线程将使得线程之间的协调变得非常棘手。比如,如果一个线程一直阻塞在一个I/O操作上,它就永远无法返回,也就无法检查自己是否已经被结束了。要正确处理这些问题,你需要利用超时循环来小心操作线程。 |
| 79 | +例子如下: |
| 80 | + |
| 81 | +.. code-block:: python |
| 82 | + |
| 83 | + class IOTask: |
| 84 | + def terminate(self): |
| 85 | + self._running = False |
| 86 | +
|
| 87 | + def run(self, sock): |
| 88 | + # sock is a socket |
| 89 | + sock.settimeout(5) # Set timeout period |
| 90 | + while self._running: |
| 91 | + # Perform a blocking I/O operation w/ timeout |
| 92 | + try: |
| 93 | + data = sock.recv(8192) |
| 94 | + break |
| 95 | + except socket.timeout: |
| 96 | + continue |
| 97 | + # Continued processing |
| 98 | + ... |
| 99 | + # Terminated |
| 100 | + return |
108 | 101 | |
|
109 | 102 |
|
110 | 103 | ----------
|
111 | 104 | 讨论
|
112 | 105 | ----------
|
113 |
| -Due to a global interpreter lock (GIL), Python threads are restricted to an execution |
114 |
| -model that only allows one thread to execute in the interpreter at any given time. For |
115 |
| -this reason, Python threads should generally not be used for computationally intensive |
116 |
| -tasks where you are trying to achieve parallelism on multiple CPUs. They are much |
117 |
| -better suited for I/O handling and handling concurrent execution in code that performs |
118 |
| -blocking operations (e.g., waiting for I/O, waiting for results from a database, etc.). |
119 |
| -Sometimes you will see threads defined via inheritance from the Thread class. For |
120 |
| -example: |
121 |
| - |
122 |
| -from threading import Thread |
123 |
| - |
124 |
| -class CountdownThread(Thread): |
125 |
| - def __init__(self, n): |
126 |
| - super().__init__() |
127 |
| - self.n = 0 |
128 |
| - def run(self): |
129 |
| - while self.n > 0: |
130 |
| - |
131 |
| - print('T-minus', self.n) |
132 |
| - self.n -= 1 |
133 |
| - time.sleep(5) |
134 |
| - |
135 |
| -c = CountdownThread(5) |
136 |
| -c.start() |
137 |
| - |
138 |
| -Although this works, it introduces an extra dependency between the code and the |
139 |
| -threading library. That is, you can only use the resulting code in the context of threads, |
140 |
| -whereas the technique shown earlier involves writing code with no explicit dependency |
141 |
| -on threading. By freeing your code of such dependencies, it becomes usable in other |
142 |
| -contexts that may or may not involve threads. For instance, you might be able to execute |
143 |
| -your code in a separate process using the multiprocessing module using code like this: |
144 |
| - |
145 |
| -import multiprocessing |
146 |
| -c = CountdownTask(5) |
147 |
| -p = multiprocessing.Process(target=c.run) |
148 |
| -p.start() |
149 |
| -... |
150 |
| - |
151 |
| -Again, this only works if the CountdownTask class has been written in a manner that is |
152 |
| -neutral to the actual means of concurrency (threads, processes, etc.). |
| 106 | +由于全局解释锁(GIL)的原因,Python 的线程被限制到同一时刻只允许一个线程执行这样一个执行模型。所以,Python 的线程更适用于处理I/O和其他需要并发执行的阻塞操作(比如等待I/O、等待从数据库获取数据等等),而不是需要多处理器并行的计算密集型任务。 |
| 107 | + |
| 108 | +有时你会看到下边这种通过继承 ``Thread`` 类来实现的线程: |
| 109 | + |
| 110 | +.. code-block:: python |
| 111 | + |
| 112 | + from threading import Thread |
| 113 | +
|
| 114 | + class CountdownThread(Thread): |
| 115 | + def __init__(self, n): |
| 116 | + super().__init__() |
| 117 | + self.n = 0 |
| 118 | + def run(self): |
| 119 | + while self.n > 0: |
| 120 | +
|
| 121 | + print('T-minus', self.n) |
| 122 | + self.n -= 1 |
| 123 | + time.sleep(5) |
| 124 | +
|
| 125 | + c = CountdownThread(5) |
| 126 | + c.start() |
| 127 | +
|
| 128 | +尽管这样也可以工作,但这使得你的代码依赖于 ``threading`` 库,所以你的这些代码只能在线程上下文中使用。上文所写的那些代码、函数都是与 ``threading`` 库无关的,这样就使得这些代码可以被用在其他的上下文中,可能与线程有关,也可能与线程无关。比如,你可以通过 ``multiprocessing`` 模块在一个单独的进程中执行你的代码: |
| 129 | + |
| 130 | +.. code-block:: python |
| 131 | + |
| 132 | + import multiprocessing |
| 133 | + c = CountdownTask(5) |
| 134 | + p = multiprocessing.Process(target=c.run) |
| 135 | + p.start() |
| 136 | +
|
| 137 | +
|
| 138 | +再次重申,这段代码仅适用于 CountdownTask 类是以独立于实际的并发手段(多线程、多进程等等)实现的情况。 |
0 commit comments