在实际开发中,我们经常需要让程序同时执行多个任务,例如:
Python 提供了多种方式来实现并发,其中 多线程 是一种简单且常用的手段。
概念 | 定义 | 特点 |
---|---|---|
进程 | 操作系统资源分配的基本单位 | 拥有独立的内存空间,开销大 |
线程 | CPU 调度的基本单位,依附于进程存在 | 共享所属进程的资源,创建销毁成本低 |
注意:
import time
def task_one():
for i in range(2):
print("Task one 正在运行...")
time.sleep(1)
def task_two():
for i in range(2):
print("Task Two 正在运行...")
time.sleep(1)
if __name__ == '__main__':
start_time = time.time()
task_one()
task_two()
end_time = time.time()
print(f"总耗时: {end_time - start_time} 秒")
输出结果:
Task one 正在运行...
Task one 正在运行...
Task Two 正在运行...
Task Two 正在运行...
总耗时: ~4秒
结论:两个函数是顺序执行的,总共耗时约 4 秒。
import threading
import time
def task_one(task_name):
for i in range(2):
print(f"{task_name} 正在运行...")
time.sleep(1)
def task_two():
for i in range(2):
print("Task Two 正在运行...")
time.sleep(1)
if __name__ == "__main__":
print("开始多线程执行任务")
start = time.time()
# 创建线程对象
thread1 = threading.Thread(target=task_one, args=("任务线程1",))
thread2 = threading.Thread(target=task_two)
# 设置守护线程(可选)
thread1.daemon = True
thread2.daemon = True
# 启动线程
thread1.start()
thread2.start()
# 阻塞主线程,等待子线程完成
thread1.join()
thread2.join()
end = time.time()
print(f"总耗时: {end - start} 秒")
输出结果:
任务线程1 正在运行...
Task Two 正在运行...
任务线程1 正在运行...
Task Two 正在运行...
总耗时: ~2秒
结论:两个任务并发执行,总耗时约为 2 秒,说明实现了并行效果。
print(thread1.name)
thread1.name = "线程1"
⚠️ 注意:setName()
方法已被弃用,推荐直接通过 .name
属性设置。
thread1.daemon = True
作用:当主线程结束时,守护线程也会自动退出。
由于线程是由 CPU 时间片调度决定的,所以它们的执行顺序是不确定的。
def task():
time.sleep(1)
print("当前线程是:", threading.current_thread().name)
if __name__ == '__main__':
for _ in range(10):
t = threading.Thread(target=task)
t.start()
现象:输出可能乱序甚至穿插,比如“当前线程是”几个字还没打完,另一个线程就开始输出。
原因:print()
不是原子操作,在多线程并发时可能出现交错输出。
li = []
def writedata():
for i in range(5):
li.append(i)
time.sleep(1)
print("写入的数据是:", li)
def readdata():
print("读取的数据是:", li)
if __name__ == "__main__":
wd = threading.Thread(target=writedata)
rd = threading.Thread(target=readdata)
wd.start()
wd.join() # 确保先写后读
rd.start()
rd.join()
说明:线程之间可以访问和修改同一个全局变量。
counter = 0
def increment():
global counter
temp = counter
temp += 1
time.sleep(0.0002)
counter = temp
def decrement():
global counter
temp = counter
temp -= 1
time.sleep(0.0002)
counter = temp
threads = []
if __name__ == "__main__":
for _ in range(50):
threads.append(threading.Thread(target=increment))
for _ in range(50):
threads.append(threading.Thread(target=decrement))
for t in threads:
t.start()
for t in threads:
t.join()
print(f"期望值为 0,实际值为: {counter}")
问题描述:最终结果不一定是 0,因为多个线程同时操作共享变量导致数据不一致。
为了避免资源竞争,我们可以使用 threading.Lock()
来保护共享资源。
import threading
import time
counter = 0
lock = threading.Lock()
def increment():
with lock:
global counter
temp = counter
temp += 1
time.sleep(0.0002)
counter = temp
def decrement():
with lock:
global counter
temp = counter
temp -= 1
time.sleep(0.0002)
counter = temp
threads = []
if __name__ == "__main__":
for _ in range(50):
threads.append(threading.Thread(target=increment))
for _ in range(50):
threads.append(threading.Thread(target=decrement))
for t in threads:
t.start()
for t in threads:
t.join()
print(f"期望值为 0,实际值为: {counter}")
说明:
with lock:
自动加锁和释放,避免死锁。✅ 推荐做法:
场景 | 建议 |
---|---|
需要并发执行任务 | 使用 threading.Thread |
控制线程生命周期 | 使用 join() 等待线程结束 |
线程间通信与共享资源 | 加锁(Lock )或使用队列(queue.Queue ) |
资源安全访问 | 使用上下文管理器 with lock: |
简化线程管理 | 可以使用 concurrent.futures.ThreadPoolExecutor |
❗️注意事项: