FiberRef

FiberRef[A] 表示对一个 A 类型值的可变引用建模。它有两个基本操作,set:将引用设置为新值;get:取回的当前值。

Ref[A] 不同,FiberRef[A] 中的值只会与当前执行中的纤程绑定。拥有同一个 FiberRef[A] 的不同纤程可以独立设置和读取参考值,而不会发生冲突。

您可以将 FiberRef 视为与 Java 的 ThreadLocal 类似。

import zio._

for {
  fiberRef <- FiberRef.make[Int](0)
  _        <- fiberRef.set(10)
  v        <- fiberRef.get
} yield v == 10

操作

FiberRef[A] 具有几乎与 Ref[A] 相同的 API。它包括一些熟知的方法,例如:

  • FiberRef#get:返回当前引用的值。
  • FiberRef#set:设置当前引用的值。
  • FiberRef#update/FiberRef#updateSome:用指定的函数更新引用值。
  • FiberRef#modify/FiberRef#modifySome:用指定的函数更新引用值,并允许该函数返回一个值。

您也可以用 locallyFiberRef 值的访问范围仅限定于给定的 effect:

for {
  correlationId <- FiberRef.make[String]("")
  v1            <- correlationId.locally("my-correlation-id")(correlationId.get)
  v2            <- correlationId.get
} yield v1 == "my-correlation-id" && v2 == ""

传递

FiberRef[A]ZIO#fork 指令中具有 copy-on-fork 的语意。

简而言之这意味着子纤程一开始就具有父纤程在 FiberRef 中的值。当子纤程改变了该值,这个变化只能被子纤程自己看到,父纤程还将保持自己的值。

for {
  fiberRef <- FiberRef.make[Int](0)
  _        <- fiberRef.set(10)
  child    <- fiberRef.get.fork
  v        <- child.join
} yield v == 10

您可以使用 Fiber#inheritRefs 方法从一个纤程中继承所有 FiberRef 的值:

for {
  fiberRef <- FiberRef.make[Int](0)
  latch    <- Promise.make[Nothing, Unit]
  fiber    <- (fiberRef.set(10) *> latch.succeed(())).fork
  _        <- latch.await
  _        <- fiber.inheritRefs
  v        <- fiberRef.get
} yield v == 10

请注意,join 会自动调用 inheritRefs。这实际上意味着以下两个效果的行为相同:

val withJoin =
for {
  fiberRef <- FiberRef.make[Int](0)
  fiber    <- fiberRef.set(10).fork
  _        <- fiber.join
  v        <- fiberRef.get
} yield v == 10
val withoutJoin =
  for {
    fiberRef <- FiberRef.make[Int](0)
    fiber    <- fiberRef.set(10)
    v        <- fiberRef.get
  } yield v == 10

此外,您可以自定义如何(如果有的话)在建立分支纤程时更新值以及在合并纤程时如何合并值。为此,请在 FiberRef#make 期间指定所需的行为:

for {
  fiberRef <- FiberRef.make(initial = 0, join = math.max)
  child    <- fiberRef.update(_ + 1).fork
  _        <- fiberRef.update(_ + 2)
  _        <- child.join
  value    <- fiberRef.get
} yield value == 2

内存安全

FiberRef 中的值在其宿主纤程结束后,会被自动垃圾收集。无法访问的FiberRef(在用户代码中没有对其的引用)中的所有特定纤程的值也会被自动垃圾回收,哪怕它们曾经在当前运行的纤程中被使用过。

Leave a Reply
Your email address will not be published.
*
*

BACK TO TOP