繁体   English   中英

Scala:在一系列期货中的一个期货失败后执行某些操作时防止竞争条件

[英]Scala: Prevent race condition when performing some action after one Future fails out of a sequence of futures

我有一个可变的期货列表:ArrayBuffer[Future[Seq[T]]]

当我的程序运行时,新的 Futures 会随着时间的推移添加到该列表中,并最终自行完成。 在每个 Future 完成后,我对其值执行一些验证。 如果该值未通过验证,我想获取可变 ArrayBuffer[Future[Seq[T]]] 中所有已完成的 Future (_.isCompleted) 以及当前值,并对它们执行一些操作。 我只希望此操作执行一次。 一旦这一切结束,我就结束我的程序,不再关心其他尚未完成或最近添加的 Futures,而这一切正在发生。

我的问题是:

  1. 我如何避免 2 个或更多 Futures 大约同时完成并验证失败并最终多次执行该操作的竞争条件。
  2. 你能发现任何其他可能发生的问题吗?

这是我当前的实现,它具有竞争条件。 我对 Futures 和 Scala 很陌生,如果有更惯用的方式来写这个,尤其是失败检查部分,建议表示赞赏。 谢谢

val futures = ArrayBuffer[Future[Seq[T]]] = ArrayBuffer()

def add(future: Future[Seq[T]]): Unit = {
    futures += future

    val failures = future.map(value => if(failsValidation) throw new ValidationException)
    failures.failed.map(e => {
        val completed = futures
            .filter(_.isCompleted)
            .map(_.value.get)
            .filter(_.isSuccess)
            .map(_.get)

        someAction(completed)
    })
}

经过一番思考,我能够自己想出一个可能的解决方案。 请记住,这没有在生产中进行测试。

一种可能的解决方案是使用标志来防止额外执行 someAction 逻辑。 为了防止其他正在运行的 Futures 继续运行,我们可以实现一个 Cancellable Futures。

Viktor Klang 有一个实现,它在给定逻辑块的情况下包装未来并返回包装的未来和取消功能。https://viktorklang.com/blog/Futures-in-Scala-protips-6.html

所以新代码看起来像这样。

// Signature of Viktor Klang's Cancellable Future - (Future[Seq[T]], () => Boolean)
val futures = ArrayBuffer[(Future[Seq[T]], () => Boolean)] = ArrayBuffer()

var alreadyDidAction = true // flag to prevent race condition

def add(future: (Future[Seq[T]], () => Boolean)): Unit = {
    futures += future

    val failures = future.map(value => if(failsValidation) throw new ValidationException)
    failures.failed
        .filter(!_.isInstanceOf[CancellationException])
        .map(_ => {
            if(alreadyDidAction) {
                alreadyDidAction = false

                futures.foreach {
                    case (_, cancel) => cancel()
                }   

                val completed = futures
                    .filter(_.isCompleted)
                    .map(_.value.get)
                    .filter(_.isSuccess)
                    .map(_.get)

                someAction(completed)
           }
        })
}

起初我认为标志上的读写操作必须包装在一个同步块中,但因为回调是在主线程上执行的,所以不会出现竞争条件。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM