Ruby 中的线程局部实例局部变量
Thread-local instance-local variable in Ruby
我想要一个本地线程(在 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
这听起来合理吗,是否有一种惯用的方法来拥有线程局部变量、实例局部变量?我不想依赖 Rails 或任何其他库。
Rails 依赖于 concurrent-ruby gem,它为您带来了很多处理 multi-threaded 和并发环境的工具。
具体可以在这里使用Concurrent::ThreadLocalVar
class:
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
请参阅上面链接的文档以获取更多使用示例。
我想要一个本地线程(在 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
这听起来合理吗,是否有一种惯用的方法来拥有线程局部变量、实例局部变量?我不想依赖 Rails 或任何其他库。
Rails 依赖于 concurrent-ruby gem,它为您带来了很多处理 multi-threaded 和并发环境的工具。
具体可以在这里使用Concurrent::ThreadLocalVar
class:
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
请参阅上面链接的文档以获取更多使用示例。