Scala中monad中的重入锁

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala中monad中的重入锁相关的知识,希望对你有一定的参考价值。

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

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

F在这里指的是一些有效的monad。

基本上我要做的是在同一个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]ReentrantReadWriteLockwriteLock。我之所以用这种方式编写代码有一些原因,所以我不想深入研究。我想了解为什么(据我的同事,至少),这可能会破坏一些东西。

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

答案

IIUC你的问题:

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

1)由于你在这里使用任意效果F,因此完全没有保证。可以编写一个F的实现来执行新线程中的每个操作。

2)即使我们假设FIO然后doSomething的身体有人可以做IO.shift。因此包括unlock在内的下一步行动将在另一个线程中发生。可能它不可能与doSomething的当前签名,但你明白了。

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

你可以看看scalaz zio STM

以上是关于Scala中monad中的重入锁的主要内容,如果未能解决你的问题,请参考以下文章

多线程——重入锁

第142篇 合约安全-重入锁

ReentrantLock (重入锁) 源码浅析

可重入锁浅谈

Java线程之Lock

ReentrantLock实现原理