在多线程编程中,锁(Lock)和同步(Synchronization)是用于防止数据竞争和确保线程安全的重要机制。以下是锁和同步的详细解释:
数据竞争问题
数据竞争是指两个或多个线程同时访问和修改共享数据,可能导致数据的不一致和错误结果。例如:
import threading
counter = 0
def increment():
global counter
for _ in range(100000):
counter += 1
threads = [threading.Thread(target=increment) for _ in range(2)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
print(counter) # 结果可能不等于200000
在这个例子中,多个线程同时修改 counter 变量,导致最终结果可能不是预期的 200000。这种情况下需要使用锁来确保只有一个线程可以访问共享资源。
锁(Lock)
锁是一种同步原语,用于防止多个线程同时访问共享资源。Python 提供了 threading.Lock
类来实现锁机制。
基本用法
创建一个锁并在需要同步的代码块前后加锁和解锁:
import threading
counter = 0
lock = threading.Lock()
def increment():
global counter
for _ in range(100000):
lock.acquire() # 加锁
counter += 1
lock.release() # 解锁
threads = [threading.Thread(target=increment) for _ in range(2)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
print(counter) # 结果将等于200000
使用 with
语句
Python 提供了 with
语句来简化锁的使用,确保在代码块结束时自动释放锁:
import threading
counter = 0
lock = threading.Lock()
def increment():
global counter
for _ in range(100000):
with lock: # 自动加锁和解锁
counter += 1
threads = [threading.Thread(target=increment) for _ in range(2)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
print(counter) # 结果将等于200000
死锁问题
死锁是指两个或多个线程互相等待对方释放锁,导致程序无法继续执行。避免死锁的一个方法是确保所有线程以相同的顺序获取多个锁。
递归锁(RLock)
有时,同一线程需要多次获取同一个锁,这种情况下可以使用 threading.RLock(递归锁),它允许同一个线程多次获取锁而不会导致死锁:
import threading
lock = threading.RLock()
def recursive_function(level):
with lock:
print(f"Level {level}")
if level > 0:
recursive_function(level - 1)
thread = threading.Thread(target=recursive_function, args=(5,))
thread.start()
thread.join()
条件变量(Condition)
条件变量是用于复杂的线程同步机制,允许线程在满足特定条件时被唤醒。它通常与锁一起使用:
import threading
condition = threading.Condition()
shared_resource = []
def producer():
with condition:
shared_resource.append(1)
condition.notify() # 唤醒等待的线程
def consumer():
with condition:
condition.wait() # 等待条件满足
item = shared_resource.pop()
print(f"Consumed {item}")
prod_thread = threading.Thread(target=producer)
cons_thread = threading.Thread(target=consumer)
cons_thread.start()
prod_thread.start()
prod_thread.join()
cons_thread.join()
信号量(Semaphore)
信号量是一种计数器,用于限制访问共享资源的线程数量。threading.Semaphore 是一个通用的同步原语:
import threading
semaphore = threading.Semaphore(3) # 最多允许3个线程同时访问
def access_resource(thread_num):
with semaphore:
print(f"Thread {thread_num} is accessing the resource")
time.sleep(1)
threads = [threading.Thread(target=access_resource, args=(i,)) for i in range(10)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
通过理解和使用这些同步原语,可以确保在多线程环境中共享资源的安全访问,避免数据竞争和不一致的问题。