有序打印
这个问题涉及对线程执行顺序的约束。
问题
假设有三个线程 t1、t2 和 t3。t1 输出 First,t2 输出 Second,t3 输出 Third。类的代码如下:
1class OrderedPrinting 2 3 def printFirst() 4 puts "First" 5 end 6 7 def printSecond() 8 puts "Second" 9 end 10 11 def printThird() 12 puts "Third" 13 14end
线程 t1 调用 printFirst(),线程 t2 调用 printSecond(),线程 t3 调用 printThird()。这些线程可以以任意顺序运行。你需要同步这些线程,使得函数 printFirst()、printSecond() 和 printThird() 按照正确的顺序执行。
程序的工作流程如下所示:

解决方案
类 OrderedPrinting 包含一个类变量 job 和 Ruby 提供的两个同步原语: mutex 和条件变量(cv)。互斥锁用于协调多个并行线程对共享数据的访问。写在 synchronize{} 块中的代码会被锁定,直到执行完成。
类变量可以在单个类的上下文中定义为全局变量。它们被该类的所有实例共享,并使用 @@ 符号声明。类变量 job 初始化为 0,以确保“First”始终最先输出。类的基本结构如下所示:
1class OrderedPrinting 2 3 def initialize 4 @mutex = Mutex.new 5 @cv = ConditionVariable.new 6 @@job = 0 7 end 8 9 def printFirst 10 end 11 12 def printSecond 13 end 14 15 def printThird 16 end 17 18end
第一个方法 printFirst() 以 mutex.synchronize 块开始,该块锁定对共享资源的访问。它输出 "First",然后将全局变量 job 加 1,表示轮到 "Second" 输出。在释放 mutex 之前,cv 会向所有等待的线程广播当前线程已完成工作,且 mutex 可被获取。
1def printFirst 2 # synchronize 确保同一时间只有一个方法操作 job。 3 @mutex.synchronize do 4 puts "First" 5 @@job += 1 6 @cv.broadcast 7 end 8end
第二个方法 printSecond() 的工作方式与 printFirst() 类似,但增加了一个 while 循环,用于检查 job 是否等于 1。如果等于,则跳出循环并输出 "Second";否则线程将在条件变量 cv 上等待。输出 "Second" 后,job 递增,并通过 cv 通知所有等待的线程。
1def printSecond 2 @mutex.synchronize do 3 # 如果 job 的值不为 1,则等待 4 while (@@job != 1) 5 @cv.wait(@mutex) 6 end 7 # 如果 job 为 1,则输出,递增,广播并退出循环 8 puts "Second" 9 @@job += 1 10 @cv.broadcast 11 end 12end
printThird() 中的 while 循环检查 job 是否等于 2。如果等于,则输出 "Third",否则进入等待状态。
1def printThird 2 @mutex.synchronize do 3 # 如果 job 的值不为 2,则等待 4 while (@@job != 2) 5 @cv.wait(@mutex) 6 end 7 # 如果 job 为 2,则输出,广播并退出循环 8 puts "Third" 9 @cv.broadcast 10 end 11end
为了测试我们的解决方案,我们将创建三个线程,每个线程都传入 OrderedPrinting 的同一个对象。 t1 将调用 printFirst(),t2 将调用 printSecond(),t3 将调用 printThird()。无论线程调用顺序如何,输出结果均按正确顺序显示。
1class OrderedPrinting 2 3 def initialize 4 @mutex = Mutex.new 5 @cv = ConditionVariable.new 6 @@job = 0 7 end 8 9 def printFirst 10 # synchronize 确保同一时间只有一个方法操作 job。 11 @mutex.synchronize do 12 puts "First" 13 @@job += 1 14 @cv.broadcast 15 end 16 end 17 18 def printSecond 19 @mutex.synchronize do 20 # 如果 job 不为 1,则等待 21 while (@@job != 1) 22 @cv.wait(@mutex) 23 end 24 # 如果 job 为 1,则输出,递增,广播并退出循环 25 puts "Second" 26 @@job += 1 27 @cv.broadcast 28 end 29 end 30 31 def printThird 32 @mutex.synchronize do 33 # 如果 job 不为 2,则等待 34 while (@@job != 2) 35 @cv.wait(@mutex) 36 end 37 # 如果 job 为 2,则输出,广播并退出循环 38 puts "Third" 39 @cv.broadcast 40 end 41 end 42 43end 44 45 46class Main 47 48 op = OrderedPrinting.new() 49 50 # 创建三个线程,它们以随机顺序执行 51 t1 = Thread.new(op) do 52 op.printFirst 53 end 54 t2 = Thread.new(op) do 55 op.printSecond 56 end 57 t3 = Thread.new(op) do 58 op.printThird 59 end 60 61 threads = [] 62 63 threads << t3 64 threads << t1 65 threads << t2 66 67 threads.each(&:join) 68end