多线程是一种允许同时运行多个任务(线程)的技术。在Python中,threading
模块提供了丰富的API来支持多线程编程,使得可以同时执行多个操作或任务。
基本概念
线程(Thread)
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
线程创建和使用
在Python中,可以通过创建Thread
类的实例来创建新线程。threading
模块提供了基本的线程创建和同步操作的方法。
-
创建线程:使用 threading.Thread()
函数并指定目标函数和参数来创建线程。 -
启动线程:调用线程实例的 start()
方法来启动线程。 -
等待线程结束:调用线程实例的 join()
方法可以使主线程等待其他线程的结束。
线程模块
类别 | 方法/属性 | 描述 |
---|---|---|
Thread | start() |
启动线程活动。 |
join(timeout=None) |
阻塞调用线程,直到线程的活动完成或超时。 | |
is_alive() |
检查线程是否仍然存活。 | |
name |
线程名称。 | |
daemon |
布尔值,表示线程是否是守护线程。 | |
Lock | acquire(blocking=True) |
获取锁,阻塞或非阻塞模式。 |
release() |
释放锁。 | |
RLock | acquire(blocking=True) |
获取锁,可在同一线程中多次获取。 |
release() |
释放锁。 | |
Condition | acquire() |
获取底层锁。 |
release() |
释放底层锁。 | |
wait(timeout=None) |
等待通知或超时。 | |
notify(n=1) |
唤醒等待此条件的一个或多个线程。 | |
notify_all() |
唤醒等待此条件的所有线程。 | |
Semaphore | acquire(blocking=True) |
获取信号量。 |
release() |
释放信号量。 | |
Event | set() |
设置事件状态为真,唤醒所有等待此事件的线程。 |
clear() |
重置事件状态为假。 | |
wait(timeout=None) |
阻塞,直到事件状态为真或超时。 | |
Timer | start() |
启动定时器,间隔指定时间后执行目标函数。 |
Barrier | wait(timeout=None) |
阻塞线程,直到达到Barrier对象初始化时设定的屏障数。 |
threading 模块创建线程
在Python中,使用threading
模块创建和启动线程的过程通常遵循以下步骤:
-
导入 threading
模块。 -
定义线程要执行的函数。 -
创建 Thread
对象,并将函数和函数的参数传递给该对象。 -
调用线程对象的 start()
方法来启动线程。
以下是一个简单的示例,演示了如何使用threading
模块创建和启动线程:
import threading
import time
# 定义一个函数,该函数将在新线程中执行
def print_numbers():
for i in range(5):
print(i)
time.sleep(1) # 暂停1秒,模拟耗时操作
# 创建一个线程对象,目标函数是`print_numbers`
thread = threading.Thread(target=print_numbers)
# 启动线程
thread.start()
# 在主线程中继续执行其他操作
for letter in 'abcde':
print(letter)
time.sleep(1.5)
# 等待线程完成
thread.join()
print("主线程和子线程都已完成执行")
在这个示例中,print_numbers
函数将在一个单独的线程中运行,而主线程将打印字母。这显示了如何同时运行两个任务:一个在主线程中,另一个在新创建的线程中。通过调用join()
方法,主线程会等待子线程完成,确保程序在所有线程都完成后才终止。
线程同步
在多线程编程中,线程同步是确保两个或多个并发线程不会同时操作共享数据或资源的过程。线程同步是为了避免由于多个线程访问共享资源而导致的数据不一致或状态冲突。Python的threading
模块提供了几种机制来帮助同步线程,如锁(Lock)、事件(Event)、条件变量(Condition)和信号量(Semaphore)。
使用锁(Lock)进行线程同步
锁是最基本的线程同步机制。当一个线程获得锁时,任何其他尝试获得该锁的线程都会被阻塞,直到锁被释放。
下面是一个使用锁进行线程同步的示例:
import threading
# 创建一个锁对象
lock = threading.Lock()
shared_resource = 0
# 定义一个函数,该函数将在多个线程中执行
def thread_function(name):
global shared_resource
# 获取锁
lock.acquire()
try:
# 模拟有一段时间需要独占资源
current_value = shared_resource
print(f"Thread {name}: starting with {current_value}")
shared_resource = current_value + 1
print(f"Thread {name}: ending with {shared_resource}")
finally:
# 释放锁
lock.release()
# 创建并启动两个线程
thread1 = threading.Thread(target=thread_function, args=(1,))
thread2 = threading.Thread(target=thread_function, args=(2,))
thread1.start()
thread2.start()
# 等待所有线程完成
thread1.join()
thread2.join()
在这个示例中,两个线程共享一个资源shared_resource
。锁lock
确保一次只有一个线程可以修改shared_resource
。通过这种方式,可以避免竞争条件,确保数据的一致性。
线程优先级队列
在Python中,线程优先级队列(Queue
)是一个线程安全的队列数据结构,用于在生产者和消费者线程之间安全地传递数据或任务。它是多线程编程中用于任务调度和线程间通信的常用工具。Queue
模块提供了几种不同类型的队列,包括先进先出队列(Queue
)、后进先出队列(LifoQueue
)、优先级队列(PriorityQueue
)等。
Queue 模块中的常用方法
Queue
模块在Python多线程编程中用于线程间的安全通信。以下是Queue
模块中的一些常用方法,以表格形式展示:
方法 | 描述 |
---|---|
put(item) |
将item 放入队列。如果队列满,则等待直到有空间。 |
get() |
从队列中移除并返回一个项目。如果队列为空,则等待直到有项目可用。 |
put_nowait(item) |
将item 放入队列,不等待。如果队列满,则抛出异常。 |
get_nowait() |
从队列中移除并返回一个项目,不等待。如果队列为空,则抛出异常。 |
empty() |
如果队列为空,返回True ;否则返回False 。 |
full() |
如果队列已满,返回True ;否则返回False 。 |
qsize() |
返回队列中的项目数。注意,在多线程情况下,返回值是近似的。 |
join() |
阻塞调用线程,直到队列中的所有项目都被处理。 |
task_done() |
用于告知队列某个任务已经处理完毕。 |
使用Queue
进行线程间通信
下面是一个使用Queue
进行线程间通信的示例:
import threading
import queue
import time
# 创建一个先进先出队列
q = queue.Queue()
# 生产者线程函数
def producer():
for i in range(5):
print(f'Produced {i}')
q.put(i) # 将项目放入队列
time.sleep(1)
# 消费者线程函数
def consumer():
while True:
item = q.get() # 从队列中移除并返回一个项目
print(f'Consumed {item}')
q.task_done() # 发出信号,表明之前入队的任务已完成
if item == 4:
break
# 创建生产者和消费者线程
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)
# 启动线程
producer_thread.start()
consumer_thread.start()
# 等待线程完成
producer_thread.join()
consumer_thread.join()
在这个示例中,生产者线程生产数字并将它们放入队列,而消费者线程从队列中取出并消费这些数字。使用Queue
确保了数据在生产者和消费者之间安全地传递。
优先级队列(PriorityQueue
)
PriorityQueue
是一种特殊的队列,其中的每个元素都有一定的优先级。优先级最高的元素首先被移除。
import queue
# 创建一个优先级队列
pq = queue.PriorityQueue()
# 向队列中添加元素,格式为(priority_number, data)
pq.put((3, 'Low priority'))
pq.put((1, 'High priority'))
pq.put((2, 'Medium priority'))
# 依次取出元素,优先级高的先出队
while not pq.empty():
item = pq.get() # 从队列中移除并返回最高优先级的项目
print(item)
这个示例中,虽然按照低、高、中优先级的顺序添加元素,但是元素是按照优先级从高到低的顺序被取出的。
实践应用
Python 多线程实践应用
Python的多线程编程可以在多种场景中实现更高效的数据处理和任务执行。以下是一些实践应用的详细描述和对应的示例代码:
1. 并发下载多个文件
多线程可用于同时下载多个文件,从而显著减少总下载时间。
示例代码
import threading
import requests
def download_file(url, filename):
print(f"开始下载 {filename}")
response = requests.get(url)
with open(filename, "wb") as file:
file.write(response.content)
print(f"完成下载 {filename}")
# 文件URLs和本地文件名
files_to_download = {
"http://example.com/file1.pdf": "file1.pdf",
"http://example.com/file2.pdf": "file2.pdf",
}
# 创建并启动线程
threads = []
for url, filename in files_to_download.items():
thread = threading.Thread(target=download_file, args=(url, filename))
threads.append(thread)
thread.start()
# 等待所有线程完成
for thread in threads:
thread.join()
print("所有文件下载完成")
2. 多线程Web爬虫
使用多线程可以同时抓取多个网页,加快数据收集过程。
示例代码
import threading
import requests
from bs4 import BeautifulSoup
def fetch_and_parse(url):
print(f"开始抓取 {url}")
response = requests.get(url)
soup = BeautifulSoup(response.content, 'html.parser')
print(f"完成抓取 {url}")
# 这里可以进行进一步的数据处理
return soup
urls = ["http://example.com/page1", "http://example.com/page2"]
threads = []
for url in urls:
thread = threading.Thread(target=fetch_and_parse, args=(url,))
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
print("所有页面抓取完成")
3. 多线程数据库操作
在处理数据库请求时,多线程可以用来并发处理多个查询或更新,提高数据处理速度。
示例代码
import threading
import time
def database_query(query):
print(f"执行查询: {query}")
# 模拟数据库操作延时
time.sleep(2)
print(f"查询完成: {query}")
queries = ["SELECT * FROM table1", "UPDATE table2 SET column='value'"]
threads = []
for query in queries:
thread = threading.Thread(target=database_query, args=(query,))
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
print("所有数据库操作完成")
多线程在需要同时执行多个任务或响应多个用户请求的应用程序中非常有用。它可以提高程序的执行效率和响应速度。Python的threading
模块使得在Python程序中创建和管理线程变得简单。