实现可以跨多个线程修改但锁数量最少的Hash的最佳方式是什么?出于这个问题的目的,您可以假设Hash将是读取繁重的。它在所有的Ruby实现中都必须是线程安全的,包括那些真正同时操作的实现,比如JRuby,并且它必须用纯Ruby编写(不允许使用C或Java )。
你可以随意提交一个天真的解决方案,但这不太可能是最好的解决方案。为优雅加分,但锁定的可能性较小,胜过较小的代码。
发布于 2009-07-04 15:54:54
好了,现在您已经指定了“threadsafe”的实际含义,下面是两个可能的实现。下面的代码将永远在MRI和JRuby中运行。无锁实现遵循最终的一致性模型,其中每个线程使用自己的散列视图,如果主线程处于变化中。需要一些小技巧来确保将所有信息存储在线程中不会泄漏内存,但这是经过处理和测试的-运行此代码时,进程大小不会增长。这两个实现都需要更多的工作才能“完成”,这意味着删除、更新等都需要一些思考,但下面两个概念中的任何一个都可以满足您的需求。
对于阅读这个帖子的人来说,认识到整个问题是JRuby独有的是非常重要的-在磁共振成像中,内置的哈希就足够了。
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
发布于 2009-07-04 02:23:39
发布基础/朴素的解决方案,只是为了提升我的Stack Overflow cred:
require 'thread'
class ConcurrentHash < Hash
def initialize
super
@mutex = Mutex.new
end
def [](*args)
@mutex.synchronize { super }
end
def []=(*args)
@mutex.synchronize { super }
end
end
发布于 2009-07-04 02:29:02
耶胡达,我记得你提到依瓦尔的设置是原子的?那么简单的复制和交换如何?
require 'thread'
class ConcurrentHash
def initialize
@reader, @writer = {}, {}
@lock = Mutex.new
end
def [](key)
@reader[key]
end
def []=(key, value)
@lock.synchronize {
@writer[key] = value
@reader, @writer = @writer, @reader
@writer[key] = value
}
end
end
https://stackoverflow.com/questions/1080993
复制相似问题