线程同步
1. 线程基础回顾
在深入探讨线程同步之前,我们先回顾一下线程的基础知识。线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。每个线程都代表一个独立的控制流,可以并发执行。
在Ruby中,线程是通过Thread类来创建和管理的。以下是一个简单的线程创建示例:
thread = Thread.new do
puts "Hello from a new thread!"
end
thread.join # 等待线程完成
2. 为什么需要线程同步?
当多个线程同时访问共享资源时,可能会出现数据竞争(race condition)。数据竞争是指多个线程同时修改同一个数据,导致最终结果不可预测。为了避免这种情况,我们需要使用线程同步机制来确保线程安全。
3. 线程同步机制
Ruby提供了多种线程同步机制,包括:
- 互斥锁(Mutex)
- 条件变量(ConditionVariable)
- 读写锁(ReadWriteLock)
- 信号量(Semaphore)
3.1 互斥锁(Mutex)
互斥锁是最常用的线程同步机制之一。它确保在任何时刻,只有一个线程可以访问被保护的代码块。
mutex = Mutex.new
thread1 = Thread.new do
mutex.lock
puts "Thread 1 is accessing the shared resource."
sleep(2) # 模拟长时间操作
mutex.unlock
end
thread2 = Thread.new do
mutex.lock
puts "Thread 2 is accessing the shared resource."
mutex.unlock
end
thread1.join
thread2.join
在这个例子中,mutex.lock和mutex.unlock之间的代码块是受保护的,确保同一时间只有一个线程可以执行这段代码。
3.2 条件变量(ConditionVariable)
条件变量通常与互斥锁一起使用,用于线程间的通信。一个线程可以等待某个条件变量,直到另一个线程发出信号。
mutex = Mutex.new
cv = ConditionVariable.new
shared_resource = 0
producer = Thread.new do
mutex.synchronize do
10.times do
shared_resource += 1
puts "Producer: #{shared_resource}"
cv.signal # 通知等待的线程
sleep(1)
end
end
end
consumer = Thread.new do
mutex.synchronize do
while shared_resource < 10
cv.wait(mutex) # 等待生产者线程的信号
puts "Consumer: #{shared_resource}"
end
end
end
producer.join
consumer.join
在这个例子中,消费者线程会等待生产者线程更新shared_resource,并通过cv.signal通知消费者线程。
3.3 读写锁(ReadWriteLock)
读写锁允许多个线程同时读取共享资源,但在写操作时只允许一个线程访问。Ruby标准库中没有直接提供读写锁,但可以通过第三方库实现。
3.4 信号量(Semaphore)
信号量用于控制对共享资源的访问数量。它允许多个线程同时访问资源,但限制总数。
require 'thread'
semaphore = Mutex.new
5.times.map do
Thread.new do
semaphore.synchronize do
puts "Thread #{Thread.current.object_id} is accessing the resource."
sleep(1)
end
end
end.each(&:join)
在这个例子中,信号量确保同时只有一个线程可以访问资源。
4. 实践练习
练习1:使用互斥锁保护共享资源
编写一个程序,创建两个线程,分别对一个共享变量进行递增操作。使用互斥锁确保线程安全。
mutex = Mutex.new
shared_variable = 0
thread1 = Thread.new do
1000.times do
mutex.lock
shared_variable += 1
mutex.unlock
end
end
thread2 = Thread.new do
1000.times do
mutex.lock
shared_variable += 1
mutex.unlock
end
end
thread1.join
thread2.join
puts "Final value of shared_variable: #{shared_variable}"
练习2:使用条件变量实现生产者-消费者模式
编写一个程序,实现一个简单的生产者-消费者模式。生产者线程生成数据并放入队列,消费者线程从队列中取出数据并处理。
mutex = Mutex.new
cv = ConditionVariable.new
queue = []
producer = Thread.new do
10.times do |i|
mutex.synchronize do
queue << i
puts "Produced: #{i}"
cv.signal
end
sleep(1)
end
end
consumer = Thread.new do
loop do
mutex.synchronize do
while queue.empty?
cv.wait(mutex)
end
item = queue.shift
puts "Consumed: #{item}"
end
end
end
producer.join
consumer.join
5. 总结
线程同步是多线程编程中的一个重要概念,确保多个线程可以安全地访问共享资源。Ruby提供了多种同步机制,包括互斥锁、条件变量、读写锁和信号量,帮助开发者编写线程安全的代码。通过理解和实践这些机制,你可以更好地掌握多线程编程的技巧。
希望这篇教程能帮助你更好地理解线程同步的概念和应用。继续探索和实践,你将在多线程编程领域取得更大的进步!