|
5 | 5 | ----------
|
6 | 6 | 问题
|
7 | 7 | ----------
|
8 |
| -todo... |
| 8 | +你想要创建和消灭一些线程为了并发执行代码. |
9 | 9 |
|
10 | 10 | ----------
|
11 | 11 | 解决方案
|
12 | 12 | ----------
|
13 |
| -todo... |
| 13 | +``threading`` 库可以用来在自己的线程中执行任意可调用的Python对象. |
| 14 | +首先, 你要创建一个 ``Thread`` 实例, 提供一个你逍遥执行的可调用对象作为目标 |
| 15 | +这里是一个简单的样例. |
| 16 | + |
| 17 | +.. code-block:: python |
| 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 | +当你创建一个thread实例之后, 除非你调用他的 ``start()`` 方法, 不然他不会启动.( ``start()`` 方法将会调用目标函数同时传入你提供的参数) |
| 33 | + |
| 34 | +线程将会在他们自己的系统级别的线程中运行(比如, 一个POSIX线程或者Windows中的线程), 系统级别的线程完全受控于操作系统. |
| 35 | +线程启动后独立运行, 直到目标函数return返回. |
| 36 | +你可以查询一个线程实例来看看他是否还在运行: |
| 37 | + |
| 38 | +.. code-block:: python |
| 39 | +
|
| 40 | + if t.is_alive(): |
| 41 | + print('Still running') |
| 42 | + else: |
| 43 | + print('Completed') |
| 44 | +
|
| 45 | +你以可以请求把他和一个线程结合(join), 这个线程将会等待他运行结束: |
| 46 | + |
| 47 | +.. code-block:: python |
| 48 | +
|
| 49 | + t.join() |
| 50 | +
|
| 51 | +解释器将会保持运行知道所有的线程终止. 长期运行的线程或者后台的永久运行的任务, 你应该考虑让他们作为守护进程. |
| 52 | +举例: |
| 53 | + |
| 54 | +.. code-block:: python |
| 55 | +
|
| 56 | + t = Thread(target=countdown, args=(10,), daemon=True) |
| 57 | + t.start() |
| 58 | +
|
| 59 | +守护进程不能被结合(join). 不管怎样, 他们将会在主线程结束时自动销毁. |
| 60 | + |
| 61 | +在这两种操作之外, 对于线程你没有太多的其他可以使用操作. |
| 62 | +比如, 这里没有什么操作可以终结一个线程, 向线程发送信号, 调整他们的时序安排, 或者其他高级的操作. |
| 63 | +如果你想要这些特性, 你要自己写. |
| 64 | + |
| 65 | +如果你想要能够终结线程, 这个线程必须规划为向一个选取的点轮询退出. |
| 66 | +比如, 你可能吧你的线程放在这样的一个class 中: |
| 67 | + |
| 68 | +.. code-block:: python |
| 69 | +
|
| 70 | + class CountdownTask: |
| 71 | + def __init__(self): |
| 72 | + self._running = True |
| 73 | +
|
| 74 | + def terminate(self): |
| 75 | + self._running = False |
| 76 | +
|
| 77 | + def run(self, n): |
| 78 | + while self._running and n > 0: |
| 79 | + print('T-minus', n) |
| 80 | + n -= 1 |
| 81 | + time.sleep(5) |
| 82 | +
|
| 83 | +
|
| 84 | + c = CountdownTask() |
| 85 | + t = Thread(target=c.run, args=(10,)) |
| 86 | + t.start() |
| 87 | + ... |
| 88 | + c.terminate() # Signal termination |
| 89 | + t.join() # Wait for actual termination (if needed) |
| 90 | +
|
| 91 | +轮询线程的退出在一个线程执行的是阻塞的操作比如I/O的时候变得棘手. 举个例子, 一个线程被不确定的I/O操作阻塞, 他可能永远也无法返回, 检查自己是否被结束. |
| 92 | +为了正确处理这样的情况, 你需要利用带有超时的循环小心的规划线程. |
| 93 | +比如 |
| 94 | + |
| 95 | +.. code-block:: python |
| 96 | +
|
| 97 | + class IOTask: |
| 98 | + def terminate(self): |
| 99 | + # sock is a socket |
| 100 | + sock.settimeout(5) # set timeout period |
| 101 | + while self._running: |
| 102 | + # Perform a blocking I/O operation w/ timeout |
| 103 | + try: |
| 104 | + data = sock.recv(8192) |
| 105 | + break |
| 106 | + except socket.timeout: |
| 107 | + continue |
| 108 | + # Continued processing |
| 109 | + ... |
| 110 | + # Terminated |
| 111 | + return |
| 112 | +
|
| 113 | +
|
| 114 | +
|
14 | 115 |
|
15 | 116 | ----------
|
16 | 117 | 讨论
|
17 | 118 | ----------
|
18 |
| -todo... |
| 119 | +由于 全局解释器锁(GIL), Python线程被限制为任何时候只允许一个线程在解释器中运行的执行模型. |
| 120 | +正因如此, python线程通常不用作计算能力加强的任务, 这些任务应该使用多CPU的并行来达到目的. |
| 121 | +多线程更适合于读写处理和处理阻塞式的并发任务.(比如, 读写等待, 等待数据库返回的结果等等) |
| 122 | + |
| 123 | +有时你看到线程通过继承 ``Thread`` 类来定义. |
| 124 | +比如 |
| 125 | + |
| 126 | +.. code-block:: python |
| 127 | +
|
| 128 | + from threading import Thread |
| 129 | + class CountdownThread(Thread): |
| 130 | + def __init__(self, n): |
| 131 | + super().__init__() |
| 132 | + self.n = n |
| 133 | +
|
| 134 | + def run(self): |
| 135 | + while self.n > 0: |
| 136 | + print('T-minus', self.n) |
| 137 | + self.n -= 1 |
| 138 | + time.sleep(5) |
| 139 | +
|
| 140 | + c = CountdownThread(5) |
| 141 | + c.start() |
| 142 | +
|
| 143 | +
|
| 144 | +尽管他能够运行, 但是这段代码使用了与 ``threading`` 库的额外的关系. |
| 145 | +换言之, 本来你只能用配置好的线程的结果, 然而这里的技巧展现出了编写不显式依赖 ``threading`` 的代码. |
| 146 | +通过将你的代码从这样的依赖中解放出来, 使得你的代码能被其他包含或者不包含多线程的上下文使用. |
| 147 | +比如, 你获取能过运行你的代码在另一个分离的进程中, 通过 ``multiprocessing`` 模块使用: |
| 148 | + |
| 149 | +.. code-block:: python |
| 150 | +
|
| 151 | + import multiprocessing |
| 152 | + c = CountdownTask(5) |
| 153 | + p = multiprocessing.Process(target=c.run) |
| 154 | + p.start() |
| 155 | + ... |
| 156 | +
|
| 157 | +再次强调, 这只在 ``CountdownTask`` 类被写在真正意义上的并发的方式下才生效(多线程, 多进程等等) |
| 158 | + |
| 159 | +---------- |
| 160 | +译者注 |
| 161 | +---------- |
| 162 | +定义 ``CountdownTask`` 部分的代码中, 原书部分为 ``self.n = 0`` , 译者运行这段代码发现有问题,认为这个地方是笔误, |
| 163 | +正确的应该是 ``self.n = n`` . |
0 commit comments