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
:用指定的函数更新引用值,并允许该函数返回一个值。
您也可以用 locally
让 FiberRef
值的访问范围仅限定于给定的 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
(在用户代码中没有对其的引用)中的所有特定纤程的值也会被自动垃圾回收,哪怕它们曾经在当前运行的纤程中被使用过。