Scala的monad中的重入锁

时间:2019-04-25 12:29:23

标签: scala locking deadlock reentrantlock reentrantreadwritelock

我的一位同事针对在某些Scala代码中使用Java ReentrantReadWriteLock作了如下说明:

  

在这里获取锁是有风险的。它是“可重入的”,但是在内部取决于线程上下文。 F可以在不同线程中运行同一计算的不同阶段。您很容易造成死锁。

F是指一些有效的单子。

基本上,我想做的是在同一monad中两次获得相同的可重入锁。

有人可以弄清楚为什么这可能是个问题吗?

该代码分为两个文件。最外面的一个:

val lock: Resource[F, Unit] = for {
  // some other resource
  _ <- store.writeLock
} yield ()

lock.use { _ => 
  for {
    // stuff
    _ <- EitherT(store.doSomething())
    // other stuff
  } yield () 
}

然后,在store中:

import java.util.concurrent.locks.{Lock, ReentrantReadWriteLock}
import cats.effect.{Resource, Sync}

private def lockAsResource[F[_]](lock: Lock)(implicit F: Sync[F]): Resource[F, Unit] =
  Resource.make {
    F.delay(lock.lock())
  } { _ =>
    F.delay(lock.unlock())
  }

private val lock = new ReentrantReadWriteLock
val writeLock: Resource[F, Unit] = lockAsResource(lock.writeLock())

def doSomething(): F[Either[Throwable, Unit]] = writeLock.use { _ =>
  // etc etc
}

两段代码中的writeLock是相同的,它是一个cats.effect.Resource[F, Unit],其中包裹着ReentrantReadWriteLock的{​​{1}}。我以这种方式编写代码有一些原因,所以我不想对此进行深入研究。我只想了解为什么(至少根据我的同事),这可能会破坏东西。

此外,我想知道Scala中是否有其他选择可以允许这样的事情而没有死锁的风险。

1 个答案:

答案 0 :(得分:2)

IIUC您的问题:

您希望与资源lock.locklock.unlock的每次交互都在同一线程中发生。

1)由于您在此处使用任意效果F,因此根本无法保证。 可以编写F的实现,以在新线程中执行每个动作。

2)即使我们假设FIO,那么doSomething的主体还是可以IO.shift做的。因此,包括unlock在内的下一个动作将在另一个线程中发生。当前的doSomething签名可能无法实现,但是您可以理解。

  

此外,我想知道Scala中是否有其他选择可以允许这样的事情而没有死锁的风险。

您可以看看scalaz zio STM