互斥锁
- 特点:
- 一次只允许一个线程进入临界区,用于保证同一时间只有一个线程访问共享资源。
- 它只有两种状态:锁定和解锁。
- 互斥锁是一种特殊的二元信号量(值为0或1)。
- 适用场景:
- 适用于控制对共享资源的独占访问,当共享资源不允许多个线程同时访问时使用。例如,对文件的写操作,同一时间只应允许一个线程写入,避免数据混乱。
- 优先选择场景举例:
- 假设一个银行账户类,有存款和取款操作。账户余额是共享资源,为了保证在存款或取款时余额数据的一致性,应优先使用互斥锁。比如在Python中使用
threading.Lock
:
import threading
class BankAccount:
def __init__(self):
self.balance = 0
self.lock = threading.Lock()
def deposit(self, amount):
self.lock.acquire()
try:
self.balance += amount
finally:
self.lock.release()
def withdraw(self, amount):
self.lock.acquire()
try:
if self.balance >= amount:
self.balance -= amount
finally:
self.lock.release()
信号量
- 特点:
- 信号量通过一个计数器来控制访问共享资源的线程数量。
- 计数器的值表示当前可用的资源数量,线程获取信号量时,计数器减1;释放信号量时,计数器加1。
- 当计数器为0时,获取信号量的线程会被阻塞。
- 适用场景:
- 适用于控制同时访问共享资源的线程数量。例如,数据库连接池,池中有固定数量的数据库连接,通过信号量来控制同时使用连接的线程数量,避免过多线程竞争连接资源。
- 优先选择场景举例:
- 假设有一个网络爬虫程序,要限制同时发起的HTTP请求数量,避免对目标服务器造成过大压力。在Python中可以使用
threading.Semaphore
:
import threading
import requests
semaphore = threading.Semaphore(5) # 允许同时5个请求
def crawl(url):
semaphore.acquire()
try:
response = requests.get(url)
print(f"爬取 {url} 成功")
finally:
semaphore.release()
urls = ["http://example.com", "http://example1.com", "http://example2.com"]
threads = []
for url in urls:
t = threading.Thread(target=crawl, args=(url,))
threads.append(t)
t.start()
for t in threads:
t.join()
条件变量
- 特点:
- 条件变量通常和互斥锁一起使用。
- 它允许线程等待某个条件满足后再继续执行。线程可以在条件变量上等待,其他线程通过改变条件并通知条件变量,使等待的线程被唤醒。
- 适用场景:
- 适用于线程之间需要基于某种条件进行同步的场景。例如,生产者 - 消费者模型中,消费者线程需要等待生产者线程生产出数据后才能消费。
- 优先选择场景举例:
- 以生产者 - 消费者模型为例,在Python中使用
threading.Condition
:
import threading
class Queue:
def __init__(self):
self.items = []
self.lock = threading.Lock()
self.condition = threading.Condition(self.lock)
def enqueue(self, item):
self.lock.acquire()
try:
self.items.append(item)
self.condition.notify()
finally:
self.lock.release()
def dequeue(self):
self.lock.acquire()
try:
while not self.items:
self.condition.wait()
return self.items.pop(0)
finally:
self.lock.release()
queue = Queue()
def producer():
for i in range(5):
queue.enqueue(i)
print(f"生产 {i}")
def consumer():
for _ in range(5):
item = queue.dequeue()
print(f"消费 {item}")
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)
producer_thread.start()
consumer_thread.start()
producer_thread.join()
consumer_thread.join()