深入理解Python多线程编程
线程基础
线程是程序中的一个执行单元,能够同时运行多个线程可以使程序完成更多任务。但在标准Python解释器中,多个线程并非真正意义上的并行执行,而是通过轮转调度实现的。
对于需要长时间等待外部事件的任务(如IO操作),使用线程可以有效提升程序效率。但对于计算密集型任务,线程可能不会带来性能提升。
顺序执行与多线程对比
默认情况下,Python代码是顺序执行的。以下是一个顺序执行的示例:
import time
start_time = time.time()
print('1. 洗壶:1min')
time.sleep(1)
print('2. 灌凉水:1min')
time.sleep(1)
print('3. 烧水:1min')
time.sleep(1)
print('4. 等水烧开:3min')
time.sleep(3)
print('5. 洗茶杯:1min')
time.sleep(1)
print('6. 放茶叶:1min')
time.sleep(1)
print('7. 泡茶:1min')
time.sleep(1)
print(f"总运行时间:{time.time() - start_time:.2f}s")
多线程实现异步操作
通过threading模块可以实现多线程。以下是一个改写后的示例:
import time
import threading
def prepare_tea():
print("5. 洗茶杯:1min")
time.sleep(1)
print("6. 放茶叶:1min")
time.sleep(1)
start_time = time.time()
print("1. 洗壶:1min")
time.sleep(1)
print("2. 灌凉水:1min")
time.sleep(1)
print("3. 烧水:1min")
time.sleep(1)
print("4. 等水烧开:3min")
time.sleep(3)
tea_thread = threading.Thread(target=prepare_tea)
tea_thread.start()
print("7. 泡茶:1min")
time.sleep(1)
print(f"总运行时间:{time.time() - start_time:.2f}s")
线程创建与管理
Python的threading模块提供了简便的线程管理接口。
基本线程创建
import threading
def download_file():
print("开始下载文件...")
time.sleep(1)
print("完成下载文件...")
def upload_file():
print("开始上传文件...")
time.sleep(1)
print("完成上传文件...")
# 创建并启动线程
download_thread = threading.Thread(target=download_file)
upload_thread = threading.Thread(target=upload_file)
download_thread.start()
upload_thread.start()
守护线程
守护线程会在主线程退出时自动终止。
import threading
import time
def upload_file():
print("开始上传文件...")
time.sleep(1)
print("完成上传文件...")
def download_file():
print("开始下载文件...")
time.sleep(1)
print("完成下载文件...")
if __name__ == '__main__':
upload_thread = threading.Thread(target=upload_file)
download_thread = threading.Thread(target=download_file)
upload_thread.setDaemon(True)
download_thread.setDaemon(True)
upload_thread.start()
download_thread.start()
print("主线程结束")
线程安全与竞争条件
当多个线程操作共享资源时,可能会引发竞争条件。
竞争条件示例
import threading
count = 0
lock = threading.Lock()
def increment():
global count
for _ in range(1000000):
lock.acquire()
try:
count += 1
finally:
lock.release()
if __name__ == '__main__':
threads = []
for _ in range(2):
thread = threading.Thread(target=increment)
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
print(f"最终计数:{count}")
死锁与锁管理
避免死锁可以通过合理使用锁和避免无限等待实现。
锁的基本用法
import threading
lock = threading.Lock()
def synchronized(func):
def wrapper(*args, **kwargs):
lock.acquire()
try:
return func(*args, **kwargs)
finally:
lock.release()
return wrapper
@synchronized
def critical_section():
print("执行关键代码...")
time.sleep(1)
thread1 = threading.Thread(target=critical_section)
thread2 = threading.Thread(target=critical_section)
thread1.start()
thread2.start()
死锁示例
import threading
lock = threading.Lock()
def deadlock():
lock.acquire()
print("等待另一个线程释放锁...")
# 这里会发生死锁,因为两个线程都在等待对方释放锁
lock.acquire()
dead_thread = threading.Thread(target=deadlock)
dead_thread.start()
dead_thread.join()
总结
- 线程优势:能够同时执行多个任务,特别适合IO密集型操作
- 线程劣势:存在竞争条件和死锁风险,需要合理使用锁机制
- 性能注意事项:对于计算密集型任务,可能需要考虑使用
multiprocessing模块