Ruby旗语?

pexxcrt2  于 2023-01-12  发布在  Ruby
关注(0)|答案(7)|浏览(101)

我正在用Ruby实现“Fair Barbershop”问题。这是一个类赋值,但我不是在寻找任何讲义。我已经疯狂地搜索了,但我似乎找不到一个Ruby实现的信号量镜像在C中找到的那些。
我知道有互斥锁,这很好,单实现,做的正是这种信号量应该做的。
然后是条件变量,我原以为这样做会很好,但看看这些,它们需要一个互斥体来处理每个等待调用,这在我看来就像我不能给信号量输入数值一样(比如,我有7个理发店,3个理发师,等等)。
我想我需要一个计数信号量,但是我觉得Ruby在它的核心中没有包含这样一个类(从我所能找到的来看)有点奇怪。有人能帮我指出正确的方向吗?

z2acfund

z2acfund1#

如果您使用的是JRuby,则可以从Java导入信号量,如this article所示。

require 'java'

java_import 'java.util.concurrent.Semaphore'

SEM = Semaphore.new(limit_of_simultaneous_threads)
SEM.acquire #To decrement the number available
SEM.release #To increment the number available
bt1cpqcv

bt1cpqcv2#

Ruby非常适合消除SysV信号量的API缺陷,而SysV信号量是最好的--它们是进程间信号量,您可以使用SEM_UNDO,这样即使SIGKILLs也不会扰乱全局状态(POSIX进程间信号量没有这个功能),使用SysV信号量,您可以同时对多个信号量执行原子操作,只要它们在同一个信号量集中。
至于线程间信号量,它们应该可以用条件变量和互斥体完全模拟(参见BernanrdoMartinez的链接了解如何模拟)。

kpbwa7wx

kpbwa7wx3#

我还发现了这个代码:https://gist.github.com/pettyjamesm/3746457
也许有人会喜欢这个选择。

blmhpbnm

blmhpbnm4#

由于concurrent-ruby是稳定的(超过1.0)并且被广泛使用,因此最好的(并且可跨Ruby实现移植)解决方案是使用它的Concurrent::Semaphore

m3eecexj

m3eecexj5#

感谢@x3ro提供的链接。它为我指明了正确的方向。然而,随着Fukumoto提供的实现(至少对于rb1.9.2)Thread.critical不可用。此外,我尝试用Thread.exclusive{}替换Thread.critical调用,结果导致了死锁。(我已经在下面链接了),它通过用互斥体::synchronize{}替换Thread.exclusive{}以及其他一些调整解决了这个问题。感谢@x3ro把我推向了正确的方向。
http://redmine.ruby-lang.org/attachments/1109/final-semaphore.patch

yqyhoc1h

yqyhoc1h6#

由于这里的其他链接对我来说不起作用,我决定快速地组合一些东西。我还没有测试过这个,所以欢迎输入和修改。它简单地基于这样的想法:互斥锁是一个二进制信号量,因此信号量是一组互斥锁。
https://gist.github.com/3439373

jxct1oxe

jxct1oxe7#

我认为,在此上下文中提到Thread::Queue可能会对其他遇到此问题的人有所帮助。
Queue是一个线程安全的工具(使用一些后台同步原语实现),可以像传统的多处理信号量一样使用,只需要一点想象力,并且它默认是预加载的,至少在ruby v3中是这样:

#!/usr/bin/ruby
# hold_your_horses.rb
q = Queue.new
wait_thread = Thread.new{
  puts "Wait for it ..."
  q.pop
  puts "... BOOM!"
}

sleep 1
puts "... click, click ..."
q.push nil

wait_thread.join

可以很简单地证明:

user@host:~/scripts$ ruby hold_your_horses.rb
Wait for it ...
... click, click ...
... BOOM!

docs for ruby v3.1表示可以使用enumerable对象初始化Queue以设置初始内容,但在我的v3.0中没有此功能。但是,如果您想要一个具有7个许可证信号量,则很容易使用以下内容填充框:

q = Queue.new
7.times{ q.push nil }

我使用Queue来实现一些工作线程之间的接力棒传递:

class WaitForBaton
  def initialize
    @q = Queue.new
  end
  def pass_baton
    @q.push nil
    sleep 0.0
  end
  def wait_for_baton
    @q.pop
  end
end

因此,线程task_master可以执行步骤onethree,线程little_helper在适当的时间介入以处理步骤two

baton = WaitForBaton.new
task_master = Thread.new{
  step_one(ARGV[0])
  baton.pass_baton
  baton.wait_for_baton
  step_three(logfile)
}

little_helper = Thread.new{
  baton.wait_for_baton
  step_two(ARGV[1])
  baton.pass_baton
}

task_master.join
little_helper.join

注意,WaitForBaton类的.pass_baton方法中的sleep 0.0是防止task_master将接力棒传递给自身所必需的:除非线程调度 * 碰巧 * 在baton.pass_baton之后立即从task_master跳离,否则接下来发生的事情是task_masterbaton.wait_for_baton-它再次将接力棒收回。sleep 0.0明确地将执行让给可能正在等待运行的任何其他线程(并且,在这种情况下,阻塞在底层Queue上)。
放弃执行不是默认行为,因为这是信号量技术的一种有点不寻常的用法--想象一下,task_master可以生成许多任务供little_helper执行,并且task_master可以在通过Thread::Queue.push([object])方法传递任务之后立即高效地返回到生成任务。

相关问题