线程同步

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提供了多种同步机制,包括互斥锁、条件变量、读写锁和信号量,帮助开发者编写线程安全的代码。通过理解和实践这些机制,你可以更好地掌握多线程编程的技巧。

希望这篇教程能帮助你更好地理解线程同步的概念和应用。继续探索和实践,你将在多线程编程领域取得更大的进步!