线程
线程 python3.8 官方文档 (opens new window)
# 线程用法
import time
import threading
import queue
# 在 CPython 中,由于存在 全局解释器锁,同一时刻只有一个线程可以执行 Python 代码
# 如果你想要同时运行多个 I/O 密集型任务,则多线程仍然是一个合适的模型
# threading.active_count(),返回当前存活的 Thread 对象的数量。 返回值与 enumerate() 所返回的列表长度一致。
# threading.current_thread(),返回当前对应调用者的控制线程的 Thread 对象。如果调用者的控制线程不是利用 threading 创建,会返回一个功能受限的虚拟线程对象。
# threading.excepthook(args, /),处理由 Thread.run() 引发的未捕获异常。
# 当线程对象一但被创建,其活动一定会因调用线程的 start() 方法开始。这会在独立的控制线程调用 run() 方法。
# 一旦线程活动开始,该线程会被认为是 '存活的' 。当它的 run() 方法终结了(不管是正常的还是抛出未被处理的异常),就不是'存活的'。 is_alive() 方法用于检查线程是否存活。
# 其他线程可以调用一个线程的 join() 方法。这会阻塞调用该方法的线程,直到被调用 join() 方法的线程终结。
# 线程有名字。名字可以传递给构造函数,也可以通过 name 属性读取或者修改。
# 如果 run() 方法引发了异常,则会调用 threading.excepthook() 来处理它。 在默认情况下,threading.excepthook() 会静默地忽略 SystemExit。
# 一个线程可以被标记成一个“守护线程”。 这个标志的意义是,当剩下的线程都是守护线程时,整个 Python 程序将会退出。
# 初始值继承于创建线程。 这个标志可以通过 daemon 特征属性或者 daemon 构造器参数来设置。
# 守护线程在程序关闭时会突然关闭。他们的资源(例如已经打开的文档,数据库事务等等)可能没有被正确释放。
# 如果你想你的线程正常停止,设置他们成为非守护模式并且使用合适的信号机制
# ==============================
# 线程类
# class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
# start() ,开始线程活动。它在一个线程里最多只能被调用一次。
# run() ,代表线程活动的方法。 你可以在子类型里重载这个方法。
# join(timeout=None) ,等待,直到线程终结。这会阻塞调用这个方法的线程,直到被调用 join() 的线程终结
# name ,只用于识别的字符串。它没有语义。多个线程可以赋予相同的名称。 初始名称由构造函数设置。
# is_alive() ,返回线程是否存活。
# daemon ,一个表示这个线程是(True)否(False)守护线程的布尔值。一定要在调用 start() 前设置好,不然会抛出 RuntimeError 。
# 初始值继承于创建线程;主线程不是守护线程,因此主线程创建的所有线程默认都是 daemon = False。
# 当没有存活的非守护线程时,整个Python程序才会退出。
# ==============================
# 锁对象
# class threading.Lock ,实现原始锁对象的类。一旦一个线程获得一个锁,会阻塞随后尝试获得锁的线程,直到它被释放;任何线程都可以释放它。
# 需要注意的是 Lock 其实是一个工厂函数,返回平台支持的具体锁类中最有效的版本的实例。
# acquire(blocking=True, timeout=-1) ,可以阻塞或非阻塞地获得锁。
# release() ,释放一个锁。这个方法可以在任何线程中调用,不单指获得锁的线程。
# locked() ,如果获得了锁则返回真值。
class MyThread(threading.Thread):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def run(self) -> None:
if self.name == '1': # name是一个字符串
time.sleep(0.5)
print('我是一个线程{}:{}'.format(self.name, threading.get_ident()))
m1 = MyThread(name=1)
m2 = MyThread(name=2)
print(m1.name) # 获取线程的name, 默认的name Thread-1(从1开始)
print(m2.name)
m1.start() # name=1时会阻塞
# m1.join() # 如果使用了join,m2会等m1执行完了后才会执行
m2.start() # name=2时不会阻塞,所以会先打印结果
time.sleep(2)
print('+' * 30)
# ==============================
# https://docs.python.org/zh-cn/3.8/library/queue.html
q = queue.Queue() # 队列
def worker():
while True:
item = q.get()
print(f'Working on {item}')
print(f'Finished {item}')
q.task_done()
# turn-on the worker thread
threading.Thread(target=worker, daemon=True).start()
# send thirty task requests to the worker
for item in range(10):
q.put(item)
print('All task requests sent\n', end='')
# block until all tasks are done
q.join()
print('All work completed')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
上次更新: 2023/09/04, 18:39:45