4

我的一位同事就ReentrantReadWriteLock在一些 Scala 代码中使用 Java 进行了如下陈述:

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

F这里指的是一些有效的单子。

基本上我要做的是在同一个单子中两次获取相同的可重入锁。

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

代码分为两个文件。最外层:

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两段代码中的 the 是相同的,它是一个cats.effect.Resource[F, Unit]包装ReentrantReadWriteLocka writeLock。我以这种方式编写代码有一些原因,所以我不想深入研究。我只是想了解为什么(至少根据我的同事的说法),这可能会破坏一些东西。

另外,我想知道 Scala 中是否有一些替代方法可以允许这样的事情而不会出现死锁的风险。

4

1 回答 1

4

IIUC your question:

You expect that for each interaction with the Resource lock.lock and lock.unlock actions happen in the same thread.

1) There is no guarantee at all since you are using arbitrary effect F here. It's possible to write an implementation of F that executes every action in a new thread.

2) Even if we assume that F is IO then the body of doSomething someone could do IO.shift. So the next actions including unlock would happen in another thread. Probably it's not possible with the current signature of doSomething but you get the idea.

Also, I'd like to know if there is some alternative in Scala that would allow something like this without the risk for deadlocks.

You can take a look at scalaz zio STM.

于 2019-04-25T23:18:32.430 回答