Skip to content

Commit 0eba1d8

Browse files
committed
第12章第一节
1 parent 095383d commit 0eba1d8

File tree

3 files changed

+235
-3
lines changed

3 files changed

+235
-3
lines changed

cookbook/c12/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__author__ = 'zz'

cookbook/c12/p01_start_stop_thread.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
__author__ = 'zz'
2+
3+
import time
4+
def countdown(n):
5+
while n > 0:
6+
print('T-minus', n)
7+
n -= 1
8+
time.sleep(5)
9+
10+
# Create and launch a thread
11+
from threading import Thread
12+
t = Thread(target=countdown, args=(10,))
13+
t.start()
14+
15+
16+
if t.is_alive():
17+
print('Still running')
18+
else:
19+
print('Completed')
20+
21+
22+
t.join()
23+
24+
25+
t = Thread(target=countdown, args=(10,), daemon=True)
26+
t.start()
27+
28+
29+
class CountdownTask:
30+
def __init__(self):
31+
self._running = True
32+
33+
def terminate(self):
34+
self._running = False
35+
36+
def run(self, n):
37+
while self._running and n > 0:
38+
print('T-minus', n)
39+
n -= 1
40+
time.sleep(5)
41+
42+
43+
c = CountdownTask()
44+
t = Thread(target=c.run, args=(10,))
45+
t.start()
46+
c.terminate() # Signal termination
47+
t.join() # Wait for actual termination (if needed)
48+
49+
50+
51+
class IOTask:
52+
def terminate(self):
53+
# sock is a socket
54+
sock.settimeout(5) # set timeout period
55+
while self._running:
56+
# Perform a blocking I/O operation w/ timeout
57+
try:
58+
data = sock.recv(8192)
59+
break
60+
except socket.timeout:
61+
continue
62+
# Continued processing
63+
# Terminated
64+
return
65+
66+
67+
from threading import Thread
68+
class CountdownThread(Thread):
69+
def __init__(self, n):
70+
super().__init__()
71+
self.n = n
72+
73+
def run(self):
74+
while self.n > 0:
75+
print('T-minus', self.n)
76+
self.n -= 1
77+
time.sleep(5)
78+
79+
c = CountdownThread(5)
80+
c.start()
81+
82+
83+
import multiprocessing
84+
c = CountdownTask(5)
85+
p = multiprocessing.Process(target=c.run)
86+
p.start()

source/c12/p01_start_stop_thread.rst

Lines changed: 148 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,159 @@
55
----------
66
问题
77
----------
8-
todo...
8+
你想要创建和消灭一些线程为了并发执行代码.
99

1010
----------
1111
解决方案
1212
----------
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+
14115
15116
----------
16117
讨论
17118
----------
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

Comments
 (0)