Python 中的并发 —— 线程的实现
摘要:本文介绍了Python中实现线程的两种方式:_thread模块和threading模块。_thread模块提供基本线程操作,而threading模块采用面向对象设计,功能更丰富。文章详细说明了如何创建线程、线程状态转换以及守护线程的实现,并通过示例代码演示了线程的创建、运行和终止过程。重点对比了_thread.start_new_thread()方法和threading.Thread类的使用方式,同时讲解了守护线程的特性及其适用场景。
目录
步骤 3:通过 time 模块的 sleep () 方法,让线程等待 2 秒
步骤 5:调用 start () 方法启动线程,控制台将打印预设的运行状态提示
步骤 6:线程执行完毕后,调用 join () 方法回收线程资源(终止线程)
Python 中的并发 —— 线程的实现
实现线程的 Python 模块
Python 中的线程也被称作轻量级进程,因为线程占用的内存远少于进程,且能支持程序同时执行多个任务。Python 中提供了两个实现线程的核心模块,分别是:
- <_thread> 模块
- <threading> 模块
两个模块的核心区别在于:<_thread> 模块将线程视作一个函数来处理,而 **<threading>** 模块采用面向对象的设计思路,把每个线程都封装为一个对象;此外,<_thread> 模块仅适用于底层线程操作,功能远不如 **<threading>** 模块丰富。
<_thread> 模块
在 Python 早期版本中,提供的是 **<thread>** 模块,但该模块已被标记为 “废弃” 许久,官方一直推荐开发者使用 **<threading>** 模块替代。因此在 Python 3 中,thread 模块被彻底移除,为了兼容版本差异,重命名为 **<_thread>** 模块。
通过 <_thread> 模块创建新线程时,需要调用其start_new_thread 方法,该方法的语法格式如下:
_thread.start_new_thread ( function, args[, kwargs] )
参数说明:
- args:传入函数的参数,以元组形式传递
- kwargs:可选参数,以字典形式为函数传递关键字参数
如果调用的函数无需传入参数,需将args 设置为空元组。
该方法调用后会立即返回,子线程随即启动,并将参数(若有)传入指定函数执行。当函数执行完毕后,对应的子线程也会随之终止。
示例
以下示例通过 <_thread> 模块的 start_new_thread() 方法创建新线程:
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: unable to start thread")
while 1:
pass
输出结果
以下输出能清晰体现 <_thread> 模块创建的多线程执行过程:
plaintext
Thread-1: Mon Apr 23 10:03:33 2018
Thread-2: Mon Apr 23 10:03:35 2018
Thread-1: Mon Apr 23 10:03:35 2018
Thread-1: Mon Apr 23 10:03:37 2018
Thread-2: Mon Apr 23 10:03:39 2018
Thread-1: Mon Apr 23 10:03:39 2018
Thread-1: Mon Apr 23 10:03:41 2018
Thread-2: Mon Apr 23 10:03:43 2018
Thread-2: Mon Apr 23 10:03:47 2018
Thread-2: Mon Apr 23 10:03:51 2018
<threading> 模块
<threading> 模块采用面向对象的实现方式,将每个线程封装为独立对象,因此相比 <_thread> 模块,它能为线程操作提供更强大、更高级的支持。该模块从 Python 2.4 版本开始内置在 Python 中。
<threading> 模块的额外方法
<threading> 模块包含了 <_thread> 模块的所有方法,同时扩展了更多实用方法,新增方法如下:
- threading.activeCount():返回当前处于活动状态的线程对象数量
- threading.currentThread():返回调用者线程控制中的线程对象
- threading.enumerate():返回当前所有活动线程对象组成的列表
为了实现线程操作,<threading> 模块提供了Thread 核心类,该类包含以下常用方法:
- run():线程的执行入口方法
- start():启动线程,底层会调用 run () 方法
- join([time]):阻塞当前线程,等待目标线程执行终止(可选参数 time 为超时时间)
- isAlive():判断线程是否仍在执行
- getName():返回线程的名称
- setName():设置线程的名称
如何通过 <threading> 模块创建线程?
通过 <threading> 模块创建新线程可遵循以下步骤:
- 定义 Thread 类的新子类;
- 若需要传入额外参数,重写 init(self [,args]) 构造方法;
- 重写 run(self [,args]) 方法,实现线程启动后需要执行的业务逻辑。
创建好 Thread 子类后,即可实例化该类,调用start() 方法启动新线程,该方法会自动调用重写后的 run() 方法。
示例
以下示例演示如何通过 <threading> 模块创建并运行新线程:
import threading
import time
exitFlag = 0
class myThread (threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print ("Starting " + self.name)
print_time(self.name, self.counter, 5)
print ("Exiting " + self.name)
def print_time(threadName, delay, counter):
while counter:
if exitFlag:
threadName.exit()
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 ("Exiting Main Thread")
输出结果
plaintext
Starting Thread-1
Starting Thread-2
Thread-1: Mon Apr 23 10:52:09 2018
Thread-1: Mon Apr 23 10:52:10 2018
Thread-2: Mon Apr 23 10:52:10 2018
Thread-1: Mon Apr 23 10:52:11 2018
Thread-1: Mon Apr 23 10:52:12 2018
Thread-2: Mon Apr 23 10:52:12 2018
Thread-1: Mon Apr 23 10:52:13 2018
Exiting Thread-1
Thread-2: Mon Apr 23 10:52:14 2018
Thread-2: Mon Apr 23 10:52:16 2018
Thread-2: Mon Apr 23 10:52:18 2018
Exiting Thread-2
Exiting Main Thread
演示线程各状态的 Python 程序
线程共有五种状态:新建、就绪、运行、等待、终止。本节将重点演示其中三种核心状态:运行、等待、终止。线程在运行状态获取并占用系统资源,在等待状态释放资源并等待调度,执行完成后进入终止状态,最终释放所有占用的资源。
以下 Python 程序将通过 start()、sleep() 和 join() 方法,分别演示线程进入运行、等待、终止状态的过程:
步骤 1:导入所需模块 threading 和 time
import threading
import time
步骤 2:定义线程执行的目标函数
def thread_states():
print("Thread entered in running state")
步骤 3:通过 time 模块的 sleep () 方法,让线程等待 2 秒
time.sleep(2)
步骤 4:创建线程实例 T1,指定执行的目标函数
T1 = threading.Thread(target=thread_states)
步骤 5:调用 start () 方法启动线程,控制台将打印预设的运行状态提示
T1.start()
# 输出:Thread entered in running state
步骤 6:线程执行完毕后,调用 join () 方法回收线程资源(终止线程)
T1.join()
Python 中启动线程的简易方式
Python 中有多种启动线程的方式,其中最简便的是将线程逻辑定义为单个函数,再将该函数作为目标,创建 threading.Thread 对象即可。执行以下代码,理解该方式的实现逻辑:
import threading
import time
import random
def Thread_execution(i):
print("Execution of Thread {} started\n".format(i))
sleepTime = random.randint(1,4)
time.sleep(sleepTime)
print("Execution of Thread {} finished".format(i))
# 循环创建4个线程
for i in range(4):
thread = threading.Thread(target=Thread_execution, args=(i,))
thread.start()
print("Active Threads:", threading.enumerate())
输出结果
plaintext
Execution of Thread 0 started
Active Threads:
[<_MainThread(MainThread, started 6040)>,
<HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
<Thread(Thread-3576, started 3932)>]
Execution of Thread 1 started
Active Threads:
[<_MainThread(MainThread, started 6040)>,
<HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
<Thread(Thread-3576, started 3932)>,
<Thread(Thread-3577, started 3080)>]
Execution of Thread 2 started
Active Threads:
[<_MainThread(MainThread, started 6040)>,
<HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
<Thread(Thread-3576, started 3932)>,
<Thread(Thread-3577, started 3080)>,
<Thread(Thread-3578, started 2268)>]
Execution of Thread 3 started
Active Threads:
[<_MainThread(MainThread, started 6040)>,
<HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
<Thread(Thread-3576, started 3932)>,
<Thread(Thread-3577, started 3080)>,
<Thread(Thread-3578, started 2268)>,
<Thread(Thread-3579, started 4520)>]
Execution of Thread 0 finished
Execution of Thread 1 finished
Execution of Thread 2 finished
Execution of Thread 3 finished
Python 中的守护线程
在实现守护线程前,需先了解守护线程的概念及使用场景。在计算机领域,守护线程是运行在后台的线程,主要处理数据发送、文件传输等辅助性服务请求,当无任务需要处理时会处于休眠状态。
普通的非守护线程也能实现后台任务,但主线程需要手动跟踪其状态;而守护线程无需主线程管理,当主线程退出时,所有守护线程会被自动终止。
注意:守护线程仅适用于非核心任务—— 这类任务即使执行中断、未完成,也不会对程序的核心逻辑产生影响。
以下是 Python 中守护线程的实现代码:
import threading
import time
def nondaemonThread():
print("starting my thread")
time.sleep(8)
print("ending my thread")
def daemonThread():
while True:
print("Hello")
time.sleep(2)
if __name__ == '__main__':
nondaemonThread = threading.Thread(target = nondaemonThread)
daemonThread = threading.Thread(target = daemonThread)
# 将线程设置为守护线程
daemonThread.setDaemon(True)
# 启动线程
daemonThread.start()
nondaemonThread.start()
上述代码中定义了两个函数:nondaemonThread() 为非守护线程的执行逻辑,打印状态后休眠 8 秒再结束;daemonThread() 为守护线程的执行逻辑,无限循环每 2 秒打印一次Hello。
通过以下输出,可清晰看出守护线程与非守护线程的执行差异:
plaintext
Hello
starting my thread
Hello
Hello
Hello
Hello
ending my thread
Hello
Hello
Hello
Hello
Hello更多推荐



所有评论(0)