TRef

TRef[A] 是可以参与 STM 事务的对不可变值的可变引用。其可变引用可以在事务内检索和设置,并被强制保证原子性,一致性并与其他事务的隔离。

TRef 在 STM 内存中发生改变时,使用了低级机制来创建事务。

创建一个 TRef

在事务内部创建TRef:

import zio._
import zio.stm._

val createTRef: STM[Nothing, TRef[Int]] = TRef.make(10)

或者在事务内创建一个TRef,然后立即提交该事务,这使您可以存储并传递一个值引用。

import zio._
import zio.stm._

val commitTRef: UIO[TRef[Int]] = TRef.makeCommit(10)

从 TRef 中取出值

从一次事务中取回结果值:

import zio._
import zio.stm._

val retrieveSingle: UIO[Int] = (for {
  tRef <- TRef.make(10)
  value <- tRef.get
} yield value).commit

或多次事务提交后取回值:

import zio._
import zio.stm._

val retrieveMultiple: UIO[Int] = for {
  tRef <- TRef.makeCommit(10)
  value <- tRef.get.commit
} yield value

给 TRef 设置一个值

设置该值将覆盖现的引用内容。

在单个交易中设置值:

import zio._
import zio.stm._

val setSingle: UIO[Int] = (for {
  tRef <- TRef.make(10)
  _ <- tRef.set(20)
  nValue <- tRef.get
} yield nValue).commit

或(在)多次交易(中多次设置):

import zio._
import zio.stm._

val setMultiple: UIO[Int] = for {
  tRef <- TRef.makeCommit(10)
  nValue <- tRef.set(20).flatMap(_ => tRef.get).commit
} yield nValue

更新 TRef 中的值

update(A => A) 函数允许根据 TRef 中旧的值计算新的值。

在单次事务中更新值:

import zio._
import zio.stm._

val updateSingle: UIO[Int] = (for {
  tRef <- TRef.make(10)
  nValue <- tRef.updateAndGet(_ + 20)
} yield nValue).commit

或(在)多次事务(中多次更新):

import zio._
import zio.stm._

val updateMultiple: UIO[Int] = for {
  tRef <- TRef.makeCommit(10)
  nValue <- tRef.updateAndGet(_ + 20).commit
} yield nValue

修改 TRef 中的值

modify(A => (B, A)): B 函数和 update 相似,但是它允许返回一些(B 类型的)信息。

在单个事务中修改值:

import zio._
import zio.stm._

val modifySingle: UIO[(String, Int)] = (for {
  tRef <- TRef.make(10)
  mValue <- tRef.modify(v => ("Zee-Oh", v + 10))
  nValue <- tRef.get
} yield (mValue, nValue)).commit

或(在)多次事务(中多次修改):

import zio._
import zio.stm._

val modifyMultiple: UIO[(String, Int)] = for {
  tRef <- TRef.makeCommit(10)
  tuple2 <- tRef.modify(v => ("Zee-Oh", v + 10)).zip(tRef.get).commit
} yield tuple2

使用例子

这是使用 TRef 在两个纤程之间传递值的例子:

import zio._
import zio.stm._

def transfer(tSender: TRef[Int],
             tReceiver: TRef[Int],
             amount: Int): UIO[Int] = {
  STM.atomically {
    for {
      _ <- tSender.get.retryUntil(_ >= amount)
      _ <- tSender.update(_ - amount)
      nAmount <- tReceiver.updateAndGet(_ + amount)
    } yield nAmount
  }
}

val transferredMoney: UIO[String] = for {
  tSender <- TRef.makeCommit(50)
  tReceiver <- TRef.makeCommit(100)
  _ <- transfer(tSender, tReceiver, 50).fork
  _ <- tSender.get.retryUntil(_ == 0).commit
  tuple2 <- tSender.get.zip(tReceiver.get).commit
  (senderBalance, receiverBalance) = tuple2
} yield s"sender: $senderBalance & receiver: $receiverBalance"

在此示例中,我们为发送者和接收者创建并提交两个事务引用,以便能够提取它们的值。在接下来的步骤中,我们创建一个原子事务,仅在发送者帐户中有足够的可用余额时才更新两个帐户。然后,我们(fork)以异步运行它。在接下去的纤程中,我们暂停它的执行直到发件人余额发生变化(在这种情况下达到零为止)。 最后,我们提取两个帐户的新值并将其合并为一个结果。

ZTRef

和 Ref[A] 类似,TRef[A] 实际上是 ZTRef[+EA, +EB, -A, +B] 类型的别名, ZTRef[+EA, +EB, -A, +B] 是一个多态的事务性引用,支持 ZRef 所提供的所有运算。有关多态引用的更多讨论,请参见 ZRef.

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

BACK TO TOP