在 JRuby 中编写线程安全代码时,希望确保我们使用了正确的同步(并且仅是必要的);具体来说,在 Puma 实例化的 Rails 应用程序中。
更新:广泛重新编辑了这个问题,非常清楚并使用我们正在实施的最新代码。此代码使用atomic
@headius (Charles Nutter) 为JRuby 编写的gem,但不确定它是否完全必要,或者以何种方式需要,对于我们在这里尝试做的事情。
这就是我们所得到的,这是过度杀伤(意思是,我们是否过度/超级工程化),或者可能不正确?
我们的宝石.rb:
require 'atomic' # gem from @headius
SUPPORTED_SERVICES = %w(serviceABC anotherSvc andSoOnSvc).freeze
module Foo
def self.included(cls)
cls.extend(ClassMethods)
cls.send :__setup
end
module ClassMethods
def get(service_name, method_name, *args)
__cached_client(service_name).send(method_name.to_sym, *args)
# we also capture exceptions here, but leaving those out for brevity
end
private
def __client(service_name)
# obtain and return a client handle for the given service_name
# we definitely want to cache the value returned from this method
# **AND**
# it is a requirement that this method ONLY be called *once PER service_name*.
end
def __cached_client(service_name)
@@_clients.value[service_name]
end
def __setup
@@_clients = Atomic.new({})
@@_clients.update do |current_service|
SUPPORTED_SERVICES.inject(Atomic.new({}).value) do |memo, service_name|
if current_services[service_name]
current_services[service_name]
else
memo.merge({service_name => __client(service_name)})
end
end
end
end
end
end
客户端.rb:
require 'ourgem'
class GetStuffFromServiceABC
include Foo
def self.get_some_stuff
result = get('serviceABC', 'method_bar', 'arg1', 'arg2', 'arg3')
puts result
end
end
上面的总结:我们有@@_clients
(一个可变的类变量,持有一个客户端的哈希),我们只想为所有可用的服务填充一次,这些服务以 service_name 为键。
由于哈希在类变量中(因此是线程安全的?),我们是否保证__client
每个服务名称的调用不会多次运行(即使 Puma 使用此类实例化多个线程以服务来自不同的所有请求用户)?如果类变量是线程安全的(以这种方式),那么也许Atomic.new({})
是不必要的?
另外,我们应该改用 anAtomic.new(ThreadSafe::Hash)
吗?或者,这不是必需的吗?
如果不需要(意思是:你认为我们至少Atomic.new
需要 s,也许还需要s ThreadSafe::Hash
),那么为什么第二个(或第三个等)线程不能在 s 之间中断,而Atomic.new(nil)
每个线程中的 s 将每个创建两个(单独的)对象?@@_clients.update do ...
Atomic.new
感谢您提供任何线程安全建议,我们在 SO 上没有看到任何直接解决此问题的问题。