我想要一个线程本地(在 Rails 应用程序的上下文中,每个请求),实例本地 var,例如假设我有一个可全局访问的对象的实例。
module Event
def self.bus
@bus ||= Bus.new
end
end
该bus
对象可以为每个请求设置元数据:
class Bus
def with_metadata(metadata, &block)
@metadata = metadata
block.call
end
# ...
end
问题在于,在线程环境中,例如 Puma 上的 Rails 应用程序,@metadata
在不同线程(即请求)之间全局共享,因此如果元数据包含 IP 地址,例如,它仅对某些请求是不正确的。
我们可以使用Thread.current
哪个是线程本地的:
class Bus
def with_metadata(metadata, &block)
Thread.current["with_metadata"] = metadata
block.call
ensure
Thread.current["with_metadata"] = nil
end
# ...
end
然而,这引入了一个问题,如果总线对象的另一个实例在同一个线程中初始化,即Event.bus
和Bus.new
,它们错误地共享“with_metadata”状态。
我的想法是object_id
在密钥中包含:
class Bus
def with_metadata(metadata, &block)
Thread.current["#{object_id}_with_metadata"] = metadata
block.call
ensure
Thread.current["#{object_id}_with_metadata"] = nil
end
# ...
end
这听起来合理吗,是否有一种惯用的方式来拥有线程本地、实例本地 var?我不想依赖 Rails 或任何其他库。
Rails 依赖于concurrent-ruby gem,它为您提供了许多工具来处理多线程和并发环境。
具体来说,您可以在Concurrent::ThreadLocalVar
此处使用该类:
class Bus
def initialize
@metadata = Concurrent::ThreadLocalVar.new
end
def metadata
@metadata.value
end
def with_metadata(metadata, &block)
@metadata.value = metadata
yield
ensure
@metadata.value = nil
end
# ...
end
有关更多使用示例,请参阅上面链接的文档。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句