【发布时间】:2019-02-27 04:38:59
【问题描述】:
我已经为 Scala 中的 futures 编写了许多不同的单元测试。 所有异步调用都使用执行上下文。 为了确保异步调用始终以相同的顺序执行,我需要延迟一些相当困难的任务并减慢测试速度。 执行者可能仍然(取决于它的实现)在其他任务之前完成一些任务。
测试具有特定执行顺序的并发代码的最佳方法是什么?例如,我有以下测试用例:
"firstSucc" should "complete the final future with the first one" in {
val executor = getExecutor
val util = getUtil
val f0 = util.async(executor, () => 10)
f0.sync
val f1 = util.async(executor, () => { delay(); 11 })
val f = f0.firstSucc(f1)
f.get should be(10)
}
其中延迟为def delay() = Thread.sleep(4000) 和sync 同步未来(调用Await.ready(future, Duration.Inf))。
这就是我想确保 f0 已经完成并且 f1 在 f0 之后完成的方式。完成 f0 是不够的,因为 firstSucc 可能正在洗牌期货。因此,f1应该延迟到f.get检查之后。
另一个想法是从承诺创建期货并在某个时间点完成它们:
"firstSucc" should "complete the final future with the first one" in {
val executor = getExecutor
val util = getUtil
val f0 = util.async(executor, () => 10)
val p = getPromise
val f1 = p.future
val f = f0.firstSucc(f1)
f.get should be(10)
p.trySuccess(11)
}
有没有更简单/更好的方法来定义执行顺序?也许是另一种可以配置提交任务顺序的执行服务? 对于这种特定情况,将第二个未来延迟到检查结果之后可能就足够了,但在某些情况下,所有未来都必须按特定顺序完成。
完整的代码可以在这里找到:https://github.com/tdauth/scala-futures-promises
这个问题可能是相关的,因为 Scala 可以使用 Java Executor Services:Controlling Task execution order with ExecutorService
【问题讨论】:
-
为什么您认为首先测试具有延迟和特定执行顺序的并发代码是一个好主意?并发运行代码中的错误通常恰好发生,因为事件的意外顺序和线程之间的内存同步失败。像
Thread.sleep(4000)这样的东西会使测试几乎毫无用处,因为 JVM 可能会在一段时间后决定同步内存,即使代码中没有任何显式同步,这意味着即使是损坏的代码也可以轻松通过您的测试。 -
@AndreyTyukin “错误是由于意外的顺序而发生的” - 是的,但编写一个测试可能仍然有用,以确保在 特定事件发生的情况下没有错误 顺序
-
@Dima 然后应该将想要测试的逻辑与并发评估策略分开,实现一个单独的顺序确定性解释器,并用它来测试逻辑。只有这样才能切换到并发实现,并测试它是否也按预期工作(无论执行顺序如何,并且没有人为延迟)。首先用并发和期货来实现一切,然后试图施加延迟和特定的顺序,这看起来更像是试图让河流逆重力流动。
-
@AndreyTyukin 是的,没关系。问题是如何测试原始实现在切换到并发后没有中断。
-
我认为这不是一个好主意,但我需要测试有特定事件顺序的案例。否则,我无法期待某个结果。也许嘲笑未来会有所帮助,我不知道。
标签: scala concurrency future execution