在 Python 中学习和管理线程与进程涉及对 threading
和 multiprocessing
模块的理解和应用。下面是这两个模块的基础知识和使用方法。
线程管理(threading 模块)
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。在 Python 中,threading
模块提供了基本的线程操作接口。
1.创建线程
使用 threading.Thread
类创建一个线程对象,传入一个可调用的对象作为 target
参数,这个对象将在新线程中运行。
import threading
import time
def print_numbers(n):
for i in range(n):
time.sleep(1)
print(i)
# 创建线程
thread = threading.Thread(target=print_numbers,args=5)
# 启动线程
thread.start()
# 等待线程完成
thread.join()
print("线程结束")
2.线程同步
由于线程间共享进程的内存,因此当多个线程要访问同一数据时,需要进行同步,以避免竞态条件。threading
模块提供了多种同步原语,如 Lock、Event、Condition、Semaphore 等。
# 使用 Lock 进行线程同步
lock = threading.Lock()
def thread_function(name):
lock.acquire()
try:
print(f"Thread {name}: starting")
finally:
lock.release()
# 创建线程时使用同一个 lock 对象
进程管理(multiprocessing 模块)
进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位。
1.创建进程
multiprocessing
模块提供了一个 Process
类来代表一个进程对象。创建和启动进程的方式与线程类似。
from multiprocessing import Process
import time
def print_numbers(n):
for i in range(n):
time.sleep(1)
print(i)
# 创建进程
process = Process(target=print_numbers,args=5)
# 启动进程
process.start()
# 等待进程结束
process.join()
print("进程结束")
2.进程间通信
进程间通信可以通过 Queue
或 Pipe
实现。这些对象允许数据在进程之间传输。
from multiprocessing import Process, Queue
def process_function(q):
q.put("Hello from child process")
q = Queue()
p = Process(target=process_function, args=(q,))
p.start()
print(q.get()) # 从队列中获取消息
p.join()
3.进程池
当需要创建和管理多个进程时,可以使用进程池来更方便地管理这些进程。
from multiprocessing import Pool
def pool_function(name):
print(f"Process {name}: starting")
with Pool(5) as p: # 创建包含5个进程的池
p.map(pool_function, range(5))
多线程和多进程的实际场景
在 Python 中实现并行计算时,多进程由于可以绕过全局解释器锁(GIL)通常更适合 CPU 密集型任务。多线程在 Python 中由于 GIL 的存在,在执行 CPU 密集型任务时通常不会提供太多的性能提升,但它们在执行 I/O 密集型任务时仍然很有用。
1.实现并行下载 (I/O密集型)
为了实现并行下载,你可以使用 Python 的 threading
模块来创建多线程程序,或者使用 multiprocessing
模块来创建多进程程序。下面是分别使用这两种方法来实现并行下载文件的简单例子。
使用多线程实现并行下载
import threading
import requests
def download_file(url, filename):
response = requests.get(url)
with open(filename, 'wb') as file:
file.write(response.content)
print(f"{filename} downloaded.")
# 定义要下载的文件列表
urls = [
"http://example.com/file1.zip",
"http://example.com/file2.zip",
# 更多文件 ...
]
# 创建并启动线程
threads = []
for i, url in enumerate(urls):
filename = f"file_{i+1}.zip"
thread = threading.Thread(target=download_file, args=(url, filename))
thread.start()
threads.append(thread)
# 等待所有线程完成
for thread in threads:
thread.join()
print("All files downloaded.")
使用多进程实现并行下载
from multiprocessing import Process
import requests
def download_file(url, filename):
response = requests.get(url)
with open(filename, 'wb') as file:
file.write(response.content)
print(f"{filename} downloaded.")
# 定义要下载的文件列表
urls = [
"http://example.com/file1.zip",
"http://example.com/file2.zip",
# 更多文件 ...
]
# 创建并启动进程
processes = []
for i, url in enumerate(urls):
filename = f"file_{i+1}.zip"
process = Process(target=download_file, args=(url, filename))
process.start()
processes.append(process)
# 等待所有进程完成
for process in processes:
process.join()
print("All files downloaded.")
2.多线程和多进程实现并行计算 (CPU密集型)
使用多进程进行并行计算
from multiprocessing import Pool
# 定义一个 CPU 密集型任务函数
def cpu_intensive_task(n):
return sum(i*i for i in range(n))
# 创建进程池
with Pool() as pool:
# 使用 map 方法来分配任务到各个进程
results = pool.map(cpu_intensive_task, [1000000, 2000000, 3000000, 4000000])
# 打印结果
for result in results:
print(result)
使用多线程进行并行计算
虽然通常不推荐使用多线程进行 CPU 密集型计算,但为了展示如何使用,下面是一个多线程的示例。
from threading import Thread
# 定义一个 CPU 密集型任务函数
def cpu_intensive_task(n):
print(sum(i*i for i in range(n)))
# 创建线程
threads = []
for n in [1000000, 2000000, 3000000, 4000000]:
thread = Thread(target=cpu_intensive_task, args=(n,))
thread.start()
threads.append(thread)
# 等待所有线程完成
for thread in threads:
thread.join()
在实际应用中:
在 CPU 密集型任务中,由于 Python 的 GIL(全局解释器锁),多线程不一定能在 CPU 密集型任务中提供性能上的提升,而多进程由于可以绕过 GIL,通常能提供更好的性能提升,它们可以在多核处理器上实现真正的并行计算。
而对于 I/O 密集型任务,多线程和多进程都可以提高效率,但多线程通常更轻量级,创建和切换的开销更小。