如何在 Ruby 中按顺序执行线程
How to execute threads in order in Ruby
我有一个作业,其中有 2 个数字(a 和 b)和一个计数 (n)。我需要首先生成随机数,这将是每个线程休眠的时间。然后,每个线程分别计算 a 和 b 的和、差、积和除法,并重复 n 次。例如:
a = 10
b = 2
n = 2
SUM: 12
DIFFERENCE: 8
PRODUCT: 20
DIVISION: 5
SUM: 12
DIFFERENCE: 8
PRODUCT: 20
DIVISION: 5
在每一行之间,程序会休眠几秒钟。顺序必须是和、差、积和除法。我不能重复使用队列或 kill/spawn 线程。我的第一个想法是使用 4 个条件变量来指示线程需要 运行 的顺序。我想到了这个:
mutex = Mutex.new
resources = Array.new(4) { ConditionVariable.new }
t1 = Thread.new do
mutex.synchronize do
puts "Thread 1"
sleep(3)
resources[0].signal
end
end
t2 = Thread.new do
mutex.synchronize do
resources[0].wait(mutex)
puts "Thread 2"
sleep(3)
resources[1].signal
end
end
t3 = Thread.new do
mutex.synchronize do
resources[1].wait(mutex)
puts "Thread 3"
sleep(3)
resources[2].signal
end
end
t4 = Thread.new do
mutex.synchronize do
resources[2].wait(mutex)
puts "Thread 4"
sleep(3)
end
end
t1.join
t2.join
t3.join
t4.join
但我遇到了这个死锁错误:main.rb:39:in 'join': No live threads left. Deadlock? (fatal)
这种方法行得通吗?我应该怎么做才能解决它?还有其他更好的方法吗?
我不是 ruby 专家,但在我使用过的所有其他语言中,名称“条件 变量”是用词不当。对于任何其他称为“变量”的东西,我们希望如果一个线程更改它,其他线程可以稍后出现并看到它已更改。那是不是条件变量的工作方式。
当线程A“notifies/signals”一个条件变量时,它将“唤醒”其他已经在等待的线程,但是如果没有其他线程发生等待在那一刻,signal/notification 什么都不做。
条件变量不记住通知。
这是我认为可能发生的情况:
t1
线程锁定互斥量,然后休眠。
其他三个线程全部启动,都在等待互斥体时被阻塞。
来自 sleep(3)
的 t1
线程 return,它向条件变量发出信号。但是,条件变量不记得通知。其他线程的 None 已经能够进行它们的 wait(mutex)
调用,因为它们都还在尝试通过 mutex.synchronize
。通知已丢失。
t1
线程离开同步块,其他线程一个接一个地进入它们的同步块,直到所有线程都在等待信号。
同时,主线程一直挂在t1.join()
。即在t1
线程结束时调用returns,但随后主线程调用t2.join()
t2
正在等待信号,t3
正在等待信号,t4
正在等待信号,主线程正在等待 t2
结束。
没有更多活动线程。
同样,不是 ruby 专家,但在所有其他语言中,使用条件变量等待某些“条件”的线程必须执行如下操作:
# The mutex prevents other threads from modifying the "condition"
# (i.e., prevents them from modifying the `sharedData`.)
mutex.lock()
while ( sharedData.doesNotSatisfyTheCondition() ) {
# The `wait()` call _temporarily_ unlocks the mutex so that other
# threads may make the condition become true, but it's _guaranteed_
# to re-lock the mutex before it returns.
conditionVar.wait(mutex)
}
# At this point, the condition is _guaranteed_ to be true.
sharedData.doSomethingThatRequiresTheConditionToBeTrue()
mutex.unlock()
这里最重要的事情是,如果条件已经为真,调用者不会等待。如果条件已经为真,则通知可能已经发生。我们错过了,现在再等,可能会等到永远。
另一件重要的事情是,在我们等待并收到通知后,我们再次检查条件。取决于编程语言的规则、操作系统和程序的体系结构; 可能 wait()
到 return 过早。
使条件变为真很简单:
mutex.lock()
sharedData.doSomethingThatMakesTheConditionTrue()
conditionVar.notify()
mutex.unlock()
我有一个作业,其中有 2 个数字(a 和 b)和一个计数 (n)。我需要首先生成随机数,这将是每个线程休眠的时间。然后,每个线程分别计算 a 和 b 的和、差、积和除法,并重复 n 次。例如:
a = 10
b = 2
n = 2
SUM: 12
DIFFERENCE: 8
PRODUCT: 20
DIVISION: 5
SUM: 12
DIFFERENCE: 8
PRODUCT: 20
DIVISION: 5
在每一行之间,程序会休眠几秒钟。顺序必须是和、差、积和除法。我不能重复使用队列或 kill/spawn 线程。我的第一个想法是使用 4 个条件变量来指示线程需要 运行 的顺序。我想到了这个:
mutex = Mutex.new
resources = Array.new(4) { ConditionVariable.new }
t1 = Thread.new do
mutex.synchronize do
puts "Thread 1"
sleep(3)
resources[0].signal
end
end
t2 = Thread.new do
mutex.synchronize do
resources[0].wait(mutex)
puts "Thread 2"
sleep(3)
resources[1].signal
end
end
t3 = Thread.new do
mutex.synchronize do
resources[1].wait(mutex)
puts "Thread 3"
sleep(3)
resources[2].signal
end
end
t4 = Thread.new do
mutex.synchronize do
resources[2].wait(mutex)
puts "Thread 4"
sleep(3)
end
end
t1.join
t2.join
t3.join
t4.join
但我遇到了这个死锁错误:main.rb:39:in 'join': No live threads left. Deadlock? (fatal)
这种方法行得通吗?我应该怎么做才能解决它?还有其他更好的方法吗?
我不是 ruby 专家,但在我使用过的所有其他语言中,名称“条件 变量”是用词不当。对于任何其他称为“变量”的东西,我们希望如果一个线程更改它,其他线程可以稍后出现并看到它已更改。那是不是条件变量的工作方式。
当线程A“notifies/signals”一个条件变量时,它将“唤醒”其他已经在等待的线程,但是如果没有其他线程发生等待在那一刻,signal/notification 什么都不做。
条件变量不记住通知。
这是我认为可能发生的情况:
t1
线程锁定互斥量,然后休眠。
其他三个线程全部启动,都在等待互斥体时被阻塞。
来自 sleep(3)
的 t1
线程 return,它向条件变量发出信号。但是,条件变量不记得通知。其他线程的 None 已经能够进行它们的 wait(mutex)
调用,因为它们都还在尝试通过 mutex.synchronize
。通知已丢失。
t1
线程离开同步块,其他线程一个接一个地进入它们的同步块,直到所有线程都在等待信号。
同时,主线程一直挂在t1.join()
。即在t1
线程结束时调用returns,但随后主线程调用t2.join()
t2
正在等待信号,t3
正在等待信号,t4
正在等待信号,主线程正在等待 t2
结束。
没有更多活动线程。
同样,不是 ruby 专家,但在所有其他语言中,使用条件变量等待某些“条件”的线程必须执行如下操作:
# The mutex prevents other threads from modifying the "condition"
# (i.e., prevents them from modifying the `sharedData`.)
mutex.lock()
while ( sharedData.doesNotSatisfyTheCondition() ) {
# The `wait()` call _temporarily_ unlocks the mutex so that other
# threads may make the condition become true, but it's _guaranteed_
# to re-lock the mutex before it returns.
conditionVar.wait(mutex)
}
# At this point, the condition is _guaranteed_ to be true.
sharedData.doSomethingThatRequiresTheConditionToBeTrue()
mutex.unlock()
这里最重要的事情是,如果条件已经为真,调用者不会等待。如果条件已经为真,则通知可能已经发生。我们错过了,现在再等,可能会等到永远。
另一件重要的事情是,在我们等待并收到通知后,我们再次检查条件。取决于编程语言的规则、操作系统和程序的体系结构; 可能 wait()
到 return 过早。
使条件变为真很简单:
mutex.lock()
sharedData.doSomethingThatMakesTheConditionTrue()
conditionVar.notify()
mutex.unlock()