python
线程中常用的两个模块为: _thread
和 threading
(推荐使用)。(注: thread
模块已经被废弃,用户可以使用 threading
模块代替,所以 python3
中不能再使用 thread
模块,为了兼容性, python3
将 thread
重命名为 _thread
)
# 一、Python 的线程使用方式
Python
中使用线程有两种方式:函数或者用类来包装线程对象。(分别对应 _thread
和 threading
模块)
# 1.1 _thread - 函数式线程
调用 _thread
模块中的 start_new_thread()
函数来产生新线程。语法如下:
_thread.start_new_thread ( function, args[, kwargs] ) |
参数说明:
- function - 线程函数。
- args - 传递给线程函数的参数,他必须是个 tuple 类型。
- kwargs - 可选参数。
使用案例如下:
import _thread | |
import time | |
# 为线程定义一个函数 | |
def print_time(threadName, delay): | |
count = 0 | |
while count < 5: | |
time.sleep(delay) | |
count += 1 | |
print("%s: %s" % (threadName, time.ctime(time.time()))) | |
# 创建两个线程 | |
try: | |
_thread.start_new_thread(print_time, ("Thread-1", 2,)) | |
_thread.start_new_thread(print_time, ("Thread-2", 4,)) | |
except: | |
print("Error: 无法启动线程") | |
while 1: | |
pass |
执行以上程序输出结果如下:
Thread-1: Tue Apr 11 14:50:13 2023 | |
Thread-2: Tue Apr 11 14:50:15 2023 | |
Thread-1: Tue Apr 11 14:50:15 2023 | |
Thread-1: Tue Apr 11 14:50:17 2023 | |
Thread-2: Tue Apr 11 14:50:19 2023 | |
Thread-1: Tue Apr 11 14:50:19 2023 | |
Thread-1: Tue Apr 11 14:50:21 2023 | |
Thread-2: Tue Apr 11 14:50:23 2023 | |
Thread-2: Tue Apr 11 14:50:27 2023 | |
Thread-2: Tue Apr 11 14:50:31 2023 |
# 1.2 threading - 类包装式线程
_thread
提供了低级别的、原始的线程以及一个简单的锁,它相比于 threading
模块的功能还是比较有限的。 threading
模块除了包含 _thread
模块中的所有方法外,还提供的其他方法:
threading.currentThread()
: 返回当前的线程变量。threading.enumerate()
: 返回一个包含正在运行的线程的list
。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。threading.activeCount()
: 返回正在运行的线程数量,与len(threading.enumerate())
有相同的结果。
除了使用方法外,线程模块同样提供了 Thread 类来处理线程,Thread 类提供了以下方法:run()
: 用以表示线程活动的方法。start()
: 启动线程活动。join([time])
: 等待至线程中止。这阻塞调用线程直至线程的join()
方法被调用中止 - 正常退出或者抛出未处理的异常 - 或者是可选的超时发生。isAlive()
: 返回线程是否活动的。getName()
: 返回线程名。setName()
: 设置线程名。
我们可以通过直接从threading.Thread
继承创建一个新的子类,并实例化后调用start()
方法启动新线程,即它调用了线程的run()
方法。使用案例如下:
import threading | |
import time | |
class myThread(threading.Thread): | |
def __init__(self, threadID, name, delay): | |
threading.Thread.__init__(self) | |
self.threadID = threadID | |
self.name = name | |
self.delay = delay | |
def run(self): | |
print("开始线程:" + self.name) | |
print_time(self.name, self.delay, 5) | |
print("退出线程:" + self.name) | |
def print_time(threadName, delay, counter): | |
while counter: | |
time.sleep(delay) | |
print("%s: %s" % (threadName, time.ctime(time.time()))) | |
counter -= 1 | |
# 创建新线程 | |
thread1 = myThread(1, "Thread-1", 1) | |
thread2 = myThread(2, "Thread-2", 2) | |
# 开启新线程 | |
thread1.start() | |
thread2.start() | |
thread1.join() | |
thread2.join() | |
print("退出主线程") |
以上程序执行结果如下:
开始线程:Thread-1 | |
开始线程:Thread-2 | |
Thread-1: Tue Apr 11 14:52:32 2023 | |
Thread-2: Tue Apr 11 14:52:33 2023 | |
Thread-1: Tue Apr 11 14:52:33 2023 | |
Thread-1: Tue Apr 11 14:52:34 2023 | |
Thread-2: Tue Apr 11 14:52:35 2023 | |
Thread-1: Tue Apr 11 14:52:35 2023 | |
Thread-1: Tue Apr 11 14:52:36 2023 | |
退出线程:Thread-1 | |
Thread-2: Tue Apr 11 14:52:37 2023 | |
Thread-2: Tue Apr 11 14:52:39 2023 | |
Thread-2: Tue Apr 11 14:52:41 2023 | |
退出线程:Thread-2 | |
退出主线程 |
# 三、线程中止
如果想要使线程在运行规定一段时间之后结束,可以通过在线程的类中添加运行状态的变量,并在其他线程中记录时间,当其他线程记录时间到达时改变子线程的运行状态变量,子线程根据判断跳出 run () 程序来结束该程序。
⚠️⚠️⚠️由于本人并没有在网上找到其他更加方便的中止线程的方法,故自己设计了这种效率较低的中止方法
import threading | |
import time | |
class myThread(threading.Thread): | |
def __init__(self, threadID, name, delay): | |
threading.Thread.__init__(self) | |
self.threadID = threadID | |
self.name = name | |
self.delay = delay | |
self.run_state = True | |
def run(self): | |
print("开始线程:" + self.name) | |
self.print_time(5) | |
print("退出线程:" + self.name) | |
def exit(self): | |
self.run_state = False | |
def print_time(self, counter): | |
while counter: | |
if self.run_state: | |
time.sleep(self.delay) | |
print("%s: %s" % (self.name, time.ctime(time.time()))) | |
counter -= 1 | |
else: | |
print("线程被中止:" + self.name) | |
break | |
# 创建新线程 | |
thread1 = myThread(1, "Thread-1", 1) | |
# 开启新线程 | |
thread1.start() | |
# 中止子线程 | |
time.sleep(3) | |
thread1.exit() |
以上程序执行结果如下:
开始线程:Thread-1 | |
Thread-1: Tue Apr 11 15:33:35 2023 | |
Thread-1: Tue Apr 11 15:33:36 2023 | |
Thread-1: Tue Apr 11 15:33:37 2023 | |
线程被中止:Thread-1 | |
退出线程:Thread-1 |
# 四、Thread.join 用法详解
join()
方法的主要作用是阻塞主线程,其格式为 thread.join(timeout)
。 timeout
参数作为可选参数,其功能是指定 thread
线程最多阻塞主线程的时间(以秒为单位),如果省略,则默认直到 thread
执行结束(进入死亡状态)才释放主线程。简单理解为 join()
了多长时间,那么主线程就会被阻塞多长时间。如下展示了 join()
的三种情况:
# 4.1 没有使用 join
此时主线程和子线程相当于齐头并进,主线程不会等待子线程执行完了再结束,即不会阻塞。
import threading | |
import time | |
class myThread(threading.Thread): | |
def __init__(self, threadID, name, delay): | |
threading.Thread.__init__(self) | |
self.threadID = threadID | |
self.name = name | |
self.delay = delay | |
def run(self): | |
print("开始线程:" + self.name) | |
print_time(self.name, self.delay, 10) | |
print("退出线程:" + self.name) | |
def print_time(threadName, delay, counter): | |
while counter: | |
time.sleep(delay) | |
print("%s: %s" % (threadName, time.ctime(time.time()))) | |
counter -= 1 | |
# 创建新线程 | |
thread1 = myThread(1, "Thread-1", 1) | |
# 开启新线程 | |
thread1.start() | |
print("退出主线程") |
以上程序执行结果如下:
开始线程:Thread-1退出主线程 | |
Thread-1: Tue Apr 11 15:35:25 2023 | |
Thread-1: Tue Apr 11 15:35:26 2023 | |
Thread-1: Tue Apr 11 15:35:27 2023 | |
Thread-1: Tue Apr 11 15:35:28 2023 | |
Thread-1: Tue Apr 11 15:35:29 2023 | |
Thread-1: Tue Apr 11 15:35:30 2023 | |
Thread-1: Tue Apr 11 15:35:31 2023 | |
Thread-1: Tue Apr 11 15:35:32 2023 | |
Thread-1: Tue Apr 11 15:35:33 2023 | |
Thread-1: Tue Apr 11 15:35:34 2023 | |
退出线程:Thread-1 |
# 4.2 使用 join 但没有设置参数(最常用)
当主线程内的子线程都 start
后,主线程会阻塞并等待子线程执行完才继续执行,直观的表现是:子线程都 start
后,主线程若还有未执行的代码,此时不会继续执行剩余代码,而是等待子线程都执行完后再执行。
import threading | |
import time | |
class myThread(threading.Thread): | |
def __init__(self, threadID, name, delay): | |
threading.Thread.__init__(self) | |
self.threadID = threadID | |
self.name = name | |
self.delay = delay | |
def run(self): | |
print("开始线程:" + self.name) | |
print_time(self.name, self.delay, 10) | |
print("退出线程:" + self.name) | |
def print_time(threadName, delay, counter): | |
while counter: | |
time.sleep(delay) | |
print("%s: %s" % (threadName, time.ctime(time.time()))) | |
counter -= 1 | |
# 创建新线程 | |
thread1 = myThread(1, "Thread-1", 1) | |
# 开启新线程 | |
thread1.start() | |
thread1.join() | |
print("退出主线程") |
以上程序执行结果如下:
开始线程:Thread-1 | |
Thread-1: Tue Apr 11 15:37:20 2023 | |
Thread-1: Tue Apr 11 15:37:21 2023 | |
Thread-1: Tue Apr 11 15:37:22 2023 | |
Thread-1: Tue Apr 11 15:37:23 2023 | |
Thread-1: Tue Apr 11 15:37:24 2023 | |
Thread-1: Tue Apr 11 15:37:25 2023 | |
Thread-1: Tue Apr 11 15:37:26 2023 | |
Thread-1: Tue Apr 11 15:37:27 2023 | |
Thread-1: Tue Apr 11 15:37:28 2023 | |
Thread-1: Tue Apr 11 15:37:29 2023 | |
退出线程:Thread-1 | |
退出主线程 |
# 4.3 有 join 并设置参数
设置了主线程最长阻塞时间,但即使是主线程结束阻塞后执行完剩余代码,子线程也会继续执行,而不会随着主线程的结束而终止。
import threading | |
import time | |
class myThread(threading.Thread): | |
def __init__(self, threadID, name, delay): | |
threading.Thread.__init__(self) | |
self.threadID = threadID | |
self.name = name | |
self.delay = delay | |
def run(self): | |
print("开始线程:" + self.name) | |
print_time(self.name, self.delay, 10) | |
print("退出线程:" + self.name) | |
def print_time(threadName, delay, counter): | |
while counter: | |
time.sleep(delay) | |
print("%s: %s" % (threadName, time.ctime(time.time()))) | |
counter -= 1 | |
# 创建新线程 | |
thread1 = myThread(1, "Thread-1", 1) | |
# 开启新线程 | |
thread1.start() | |
thread1.join(3) | |
print("退出主线程") |
以上程序执行结果如下:
开始线程:Thread-1 | |
Thread-1: Tue Apr 11 15:37:56 2023 | |
Thread-1: Tue Apr 11 15:37:57 2023 | |
退出主线程 | |
Thread-1: Tue Apr 11 15:37:58 2023 | |
Thread-1: Tue Apr 11 15:37:59 2023 | |
Thread-1: Tue Apr 11 15:38:00 2023 | |
Thread-1: Tue Apr 11 15:38:01 2023 | |
Thread-1: Tue Apr 11 15:38:02 2023 | |
Thread-1: Tue Apr 11 15:38:03 2023 | |
Thread-1: Tue Apr 11 15:38:04 2023 | |
Thread-1: Tue Apr 11 15:38:05 2023 | |
退出线程:Thread-1 |