跳到主要内容

互斥锁与信号量

介绍

在多线程编程中,多个线程可能会同时访问共享资源,这可能导致数据不一致或竞态条件(Race Condition)的问题。为了解决这些问题,操作系统提供了两种重要的同步机制:互斥锁(Mutex)信号量(Semaphore)

  • 互斥锁:用于确保同一时间只有一个线程可以访问共享资源。
  • 信号量:用于控制对多个共享资源的访问,或者用于线程间的同步。

接下来,我们将逐步讲解这两种机制的原理、使用方法以及实际应用场景。


互斥锁(Mutex)

什么是互斥锁?

互斥锁是一种用于保护共享资源的同步机制。它的核心思想是:当一个线程获得了互斥锁后,其他线程必须等待该线程释放锁,才能继续访问共享资源。

互斥锁的使用

以下是一个使用互斥锁的简单示例(以 Python 为例):

python
import threading

# 共享资源
counter = 0
# 创建互斥锁
lock = threading.Lock()

def increment_counter():
global counter
for _ in range(100000):
lock.acquire() # 获取锁
counter += 1
lock.release() # 释放锁

# 创建两个线程
thread1 = threading.Thread(target=increment_counter)
thread2 = threading.Thread(target=increment_counter)

# 启动线程
thread1.start()
thread2.start()

# 等待线程完成
thread1.join()
thread2.join()

print(f"Final counter value: {counter}")

输出:

Final counter value: 200000

在这个例子中,如果没有互斥锁,两个线程可能会同时修改 counter,导致最终结果小于 200000。通过使用互斥锁,我们确保了每次只有一个线程可以修改 counter


信号量(Semaphore)

什么是信号量?

信号量是一种更通用的同步机制,它可以用来控制对多个共享资源的访问。信号量维护了一个计数器,表示当前可用的资源数量。线程可以通过 acquire() 减少计数器,通过 release() 增加计数器。

信号量的使用

以下是一个使用信号量的示例(以 Python 为例):

python
import threading

# 创建信号量,初始值为 2
semaphore = threading.Semaphore(2)

def access_resource(thread_id):
print(f"Thread {thread_id} is waiting to access the resource")
semaphore.acquire()
print(f"Thread {thread_id} is accessing the resource")
# 模拟资源访问
threading.Event().wait(1)
print(f"Thread {thread_id} is releasing the resource")
semaphore.release()

# 创建多个线程
threads = []
for i in range(5):
thread = threading.Thread(target=access_resource, args=(i,))
threads.append(thread)
thread.start()

# 等待所有线程完成
for thread in threads:
thread.join()

输出:

Thread 0 is waiting to access the resource
Thread 0 is accessing the resource
Thread 1 is waiting to access the resource
Thread 1 is accessing the resource
Thread 2 is waiting to access the resource
Thread 3 is waiting to access the resource
Thread 4 is waiting to access the resource
Thread 0 is releasing the resource
Thread 2 is accessing the resource
Thread 1 is releasing the resource
Thread 3 is accessing the resource
Thread 2 is releasing the resource
Thread 4 is accessing the resource
Thread 3 is releasing the resource
Thread 4 is releasing the resource

在这个例子中,信号量的初始值为 2,表示最多允许两个线程同时访问资源。其他线程必须等待信号量释放后才能继续。


互斥锁与信号量的区别

特性互斥锁(Mutex)信号量(Semaphore)
资源数量只能保护一个资源可以保护多个资源
线程关系同一时间只有一个线程同一时间可以有多个线程
主要用途保护共享资源控制资源访问或线程同步

实际应用场景

互斥锁的应用

  • 银行账户操作:多个线程同时修改账户余额时,使用互斥锁确保数据一致性。
  • 文件写入:多个线程同时写入同一个文件时,使用互斥锁避免文件损坏。

信号量的应用

  • 数据库连接池:信号量可以限制同时访问数据库的连接数。
  • 生产者-消费者问题:信号量用于同步生产者和消费者线程,确保缓冲区不会溢出或为空。

总结

互斥锁和信号量是多线程编程中非常重要的同步机制。互斥锁用于保护共享资源,确保同一时间只有一个线程可以访问;信号量则用于控制对多个资源的访问或实现线程间的同步。

通过合理使用这两种机制,可以有效避免竞态条件,确保程序的正确性和稳定性。


附加资源与练习

推荐资源

练习

  1. 修改上面的互斥锁示例,尝试去掉锁,观察输出结果的变化。
  2. 实现一个生产者-消费者模型,使用信号量来同步生产者和消费者线程。
  3. 研究其他编程语言(如 C、Java)中的互斥锁和信号量实现,并尝试编写代码。
提示

如果你对多线程编程感兴趣,可以进一步学习条件变量(Condition Variable)和读写锁(Read-Write Lock)等高级同步机制。