实现可跨多个线程修改但使用最少锁数的哈希的最佳方法是什么。出于这个问题的目的,您可以假设哈希将是重读的。它在所有 Ruby 实现中都必须是线程安全的,包括那些以真正同步的方式运行的实现,例如 JRuby,并且它必须用纯 Ruby 编写(不允许使用 C 或 Java)。
请随意提交一个总是锁定的简单解决方案,但这不太可能是最佳解决方案。优雅点,但锁定的可能性较小胜过较小的代码。
最佳答案
好的,现在您已经指定了“线程安全”的实际含义,下面是两个可能的实现。以下代码将在 MRI 和 JRuby 中永远运行。无锁实现遵循最终一致性模型,如果主线程不断变化,每个线程都使用它自己的哈希 View 。需要一些小技巧来确保将所有信息存储在线程中不会泄漏内存,但这已经过处理和测试——运行此代码时进程大小不会增加。这两种实现都需要更多的工作才能“完成”,这意味着删除、更新等需要一些思考,但以下两个概念中的任何一个都可以满足您的要求。
对于阅读此主题的人来说,重要的是要意识到整个问题是 JRuby 独有的——在 MRI 中,内置哈希就足够了。
module Cash
def Cash.new(*args, &block)
env = ENV['CASH_IMPL']
impl = env ? Cash.const_get(env) : LocklessImpl
klass = defined?(JRUBY_VERSION) ? impl : ::Hash
klass.new(*args)
end
class LocklessImpl
def initialize
@hash = {}
end
def thread_hash
thread = Thread.current
thread[:cash] ||= {}
hash = thread[:cash][thread_key]
if hash
hash
else
hash = thread[:cash][thread_key] = {}
ObjectSpace.define_finalizer(self){ thread[:cash].delete(thread_key) }
hash
end
end
def thread_key
[Thread.current.object_id, object_id]
end
def []=(key, val)
time = Time.now.to_f
tuple = [time, val]
@hash[key] = tuple
thread_hash[key] = tuple
val
end
def [](key)
# check the master value
#
val = @hash[key]
# someone else is either writing the key or it has never been set. we
# need to invalidate our own copy in either case
#
if val.nil?
thread_val = thread_hash.delete(key)
return(thread_val ? thread_val.last : nil)
end
# check our own thread local value
#
thread_val = thread_hash[key]
# in this case someone else has written a value that we have never seen so
# simply return it
#
if thread_val.nil?
return(val.last)
end
# in this case there is a master *and* a thread local value, if the master
# is newer juke our own cached copy
#
if val.first > thread_val.first
thread_hash.delete(key)
return val.last
else
return thread_val.last
end
end
end
class LockingImpl < ::Hash
require 'sync'
def initialize(*args, &block)
super
ensure
extend Sync_m
end
def sync(*args, &block)
sync_synchronize(*args, &block)
end
def [](key)
sync(:SH){ super }
end
def []=(key, val)
sync(:EX){ super }
end
end
end
if $0 == __FILE__
iteration = 0
loop do
n = 42
hash = Cash.new
threads =
Array.new(10) {
Thread.new do
Thread.current.abort_on_exception = true
n.times do |key|
hash[key] = key
raise "#{ key }=nil" if hash[key].nil?
end
end
}
threads.map{|thread| thread.join}
puts "THREADSAFE: #{ iteration += 1 }"
end
end
关于ruby - 纯 Ruby 并发哈希,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1080993/