Python 中的多线程允许在一个单一进程中并发运行多个线程,这也被称为基于线程的并行性。这意味着一个程序可以同时执行多个任务,从而提高其效率和响应性。
Python 中的多线程特别适用于多个 I/O 密集型操作,而不是对于需要大量计算的任务。
通常情况下,计算机程序是顺序执行指令的,从头到尾。而多线程则是将主任务分成一个以上的子任务,并以重叠的方式执行它们。
与进程的比较
操作系统能够处理多个并发进程。它为每个进程分配独立的内存空间,因此一个进程无法访问或写入其他进程的空间。
另一方面,线程可以被认为是单个程序中的轻量级子进程,它们共享分配给它们的内存空间,便于更轻松的通信和数据共享。由于它们是轻量级的,并不需要太多的内存开销;因此它们比进程更经济。
多线程
进程总是以一个线程(主线程)开始。根据需要,可以启动一个新的线程并将子任务委托给它。此时,两条线程以重叠的方式工作。当分配给辅助线程的任务完成后,它与主线程合并。
线程有一个开始、一个执行序列以及一个结论。它有一个指令指针,跟踪它当前在其上下文中运行的位置。
它可以被抢占(中断)
它可以暂时被挂起(也称为睡眠),而其他线程正在运行——这称为让步。
Python 中处理线程的模块
Python 的标准库提供了两个主要的模块来管理线程:_thread 和 threading。
_thread 模块
_thread 模块,也被称为低级线程模块,自从 Python 2 版本以来就是标准库的一部分。它提供了一个基本的线程管理 API,支持在一个共享的全局数据空间中并发执行线程。该模块包括简单的锁(互斥锁)用于同步目的。
threading 模块
threading 模块在 Python 2.4 中引入,建立在 _thread 基础之上,提供了一个更高层次且更为全面的线程 API。它提供了强大的工具来管理线程,使得在 Python 应用中更容易地使用线程。
threading 模块的关键特性
threading 模块暴露了 _thread 模块的所有方法,并提供了一些附加的方法:
-
threading.activeCount() — 返回活跃线程对象的数量。
-
threading.currentThread() — 返回调用者线程控制下的线程对象数量。
-
threading.enumerate() — 返回当前所有活跃线程对象的列表。
此外,threading 模块还包含了实现了线程功能的 Thread 类。Thread 类提供的方法如下:
-
-
start() — start() 方法通过调用 run 方法来启动线程。
-
join([time]) — join() 等待线程终止。
-
isAlive() — isAlive() 方法检查线程是否仍在执行。
-
getName() — getName() 方法返回线程的名称。
-
setName() — setName() 方法设置线程的名称。
启动一个新的线程
在 Python 中创建并启动一个新的线程,可以使用低级的 _thread 模块或高级的 threading 模块。通常推荐使用 threading 模块,因为它提供了额外的功能并且易于使用。下面可以看到两种方法。
使用 _thread 模块启动新线程
_thread 模块的 start_new_thread() 方法提供了一种基本的方式来创建和启动新线程。这个方法在 Linux 和 Windows 上提供了一种快速高效的方式来创建新的线程。以下是方法的语法:
thread.start_new_thread(function, args[, kwargs])
这个方法调用会立即返回,并且新线程开始执行指定的函数及其参数。当函数返回时,线程终止。
示例
下面的示例展示了如何使用 _thread 模块创建和运行线程。每个线程运行 print_name 函数,带有不同的参数。time.sleep(0.5) 调用确保主程序等待线程完成执行后再退出。
import _thread
import time
def print_name(name, *arg):
print(name, *arg)
name="Tutorialspoint..."
_thread.start_new_thread(print_name, (name, 1))
_thread.start_new_thread(print_name, (name, 1, 2))
time.sleep(0.5)
当上述代码被执行时,会产生以下结果:
Tutorialspoint... 1
Tutorialspoint... 1 2
尽管对于低级线程非常有效,但 _thread 模块相比提供了更多特性和更高层级线程管理的 threading 模块来说是有限的。
使用 threading 模块启动新线程
threading 模块提供了 Thread 类,用于创建和管理线程。
以下是使用 threading 模块启动新线程的一些步骤:
-
-
然后使用 Thread 类通过传递目标函数及其参数来创建一个 Thread 对象。
-
调用 Thread 对象上的 start 方法来开始执行。
-
可选地,调用 join 方法来等待线程完成后再继续。
示例
下面的示例展示了如何使用 threading 模块创建和启动线程。它运行一个函数 print_name,打印一个名字以及一些参数。这个示例创建了两个线程,使用 start() 方法启动它们,并使用 join 方法等待它们完成。
import threading
import time
def print_name(name, *args):
print(name, *args)
name = "Tutorialspoint..."
thread1 = threading.Thread(target=print_name, args=(name, 1))
thread2 = threading.Thread(target=print_name, args=(name, 1, 2))
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print("Threads are finished...exiting")
当上述代码被执行时,会产生以下结果:
Tutorialspoint... 1
Tutorialspiont... 1 2
Threads are finished...exiting
线程同步
Python 提供的 threading 模块包含了一个简单易实现的锁定机制,允许你同步线程。通过调用 Lock() 方法创建一个新的锁。
新锁对象的 acquire(blocking) 方法用于强制线程同步运行。可选的 blocking 参数使你能够控制线程是否等待获取锁。
如果 blocking 设置为 0,线程无法获取锁时立即返回 0 值,获取到锁则返回 1。如果 blocking 设置为 1,线程会阻塞并等待锁被释放。
新锁对象的 release() 方法用于在不再需要锁时释放它。
示例
import threading
import time
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)
threadLock.acquire()
print_time(self.name, self.counter, 3)
threadLock.release()
def print_time(threadName, delay, counter):
while counter:
time.sleep(delay)
print ("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
threadLock = threading.Lock()
threads = []
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
thread1.start()
thread2.start()
threads.append(thread1)
threads.append(thread2)
for t in threads:
t.join()
print ("Exiting Main Thread")
当上述代码被执行时,会产生以下结果:
Starting Thread-1
Starting Thread-2
Thread-1: Thu Mar 21 09:11:28 2013
Thread-1: Thu Mar 21 09:11:29 2013
Thread-1: Thu Mar 21 09:11:30 2013
Thread-2: Thu Mar 21 09:11:32 2013
Thread-2: Thu Mar 21 09:11:34 2013
Thread-2: Thu Mar 21 09:11:36 2013
Exiting Main Thread
Python 中的多线程优先队列
Queue 模块允许你创建一个新的队列对象,该对象可以保存特定数量的项。以下是用于控制队列的方法:
-
get() — get() 从队列中移除并返回一项。
-
-
qsize() — qsize() 返回当前在队列中的项数。
-
empty() — empty() 如果队列为空则返回 True,否则返回 False。
-
full() — full() 如果队列已满则返回 True,否则返回 False。
示例
下面的例子演示了如何使用 Queue 模块创建一个多线程优先队列。在这个例子中,我们创建了三个线程,它们从队列中取出数据项进行处理。
import queue
import threading
import time
exitFlag = 0
class myThread (threading.Thread):
def __init__(self, threadID, name, q):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.q = q
def run(self):
print ("Starting " + self.name)
process_data(self.name, self.q)
print ("Exiting " + self.name)
def process_data(threadName, q):
while not exitFlag:
queueLock.acquire()
if not workQueue.empty():
data = q.get()
queueLock.release()
print ("%s processing %s" % (threadName, data))
else:
queueLock.release()
time.sleep(1)
threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = queue.Queue(10)
threads = []
threadID = 1
for tName in threadList:
thread = myThread(threadID, tName, workQueue)
thread.start()
threads.append(thread)
threadID += 1
queueLock.acquire()
for word in nameList:
workQueue.put(word)
queueLock.release()
while not workQueue.empty():
pass
exitFlag = 1
for t in threads:
t.join()
print ("Exiting Main Thread")
当上述代码被执行时,会产生以下结果:
Starting Thread-1
Starting Thread-2
Starting Thread-3
Thread-1 processing One
Thread-2 processing Two
Thread-3 processing Three
Thread-1 processing Four
Thread-2 processing Five
Exiting Thread-3
Exiting Thread-1
Exiting Thread-2
Exiting Main Thread