【问题标题】:Prevent concurrent execution of part of two suspend functions防止同时执行两个挂起函数的一部分
【发布时间】:2021-08-21 22:44:47
【问题描述】:

我有两个在 ViewModel 中并行启动的挂起方法:

init {
    viewModelScope.launch(Dispatchers.Default) {
        launch { loadTotalCirculation() }
        launch { loadMarketPrice() }
    }
}

private suspend fun loadTotalCirculation() {
    val totalCirculation = bitcoinChartsRepository.getTotalCirculation(5, TimeUnit.HOURS)
    _viewState.value = _viewState.value.copy(totalCirculation = chartViewEntityMapper(totalCirculation))
}

private suspend fun loadMarketPrice() {
    val marketPrice = bitcoinChartsRepository.getMarketPrice(27, TimeUnit.DAYS)
    _viewState.value = _viewState.value.copy(marketPrice = chartViewEntityMapper(marketPrice))
}

但是,我想防止在我的两种方法中同时执行 _viewState.value = _viewState.value.copy... 部分。实现这一目标的最佳方法是什么?

【问题讨论】:

  • 您可以使用Mutex,但当您使用viewModelScope 时,并发性不应该成为问题,因为它由Dispatchers.Main.immediate 支持,因此两种值的修改都将在主UI线程上发生.
  • 嗨帕维尔,这是一个很好的观点!但是,我在默认调度程序上启动协程,因此它们可以同时运行。我现在已经更新了上面的代码。

标签: android kotlin flow stateflow


【解决方案1】:

同步并行协程有多种可能性。可能最简单的方法是创建一个单线程上下文,类似于主线程或使用Mutex。请注意,这个互斥锁是专门为 couroutines 设计的,它不是来自 Java 标准库的东西。

单线程上下文:

val context = Executors.newSingleThreadExecutor().asCoroutineDispatcher()

private suspend fun loadTotalCirculation() {
    val totalCirculation = bitcoinChartsRepository.getTotalCirculation(5, TimeUnit.HOURS)
    withContext (context) {
        _viewState.value = _viewState.value.copy(totalCirculation = chartViewEntityMapper(totalCirculation))
    }
}

private suspend fun loadMarketPrice() {
    val marketPrice = bitcoinChartsRepository.getMarketPrice(27, TimeUnit.DAYS)
    withContext (context) {
        _viewState.value = _viewState.value.copy(marketPrice = chartViewEntityMapper(marketPrice))
    }
}

或者,您可以通过 withContext(Dispatchers.Main) 重用主线程,而不是创建自己的线程。

互斥体:

val mutex = Mutex()

private suspend fun loadTotalCirculation() {
    val totalCirculation = bitcoinChartsRepository.getTotalCirculation(5, TimeUnit.HOURS)
    mutex.withLock {
        _viewState.value = _viewState.value.copy(totalCirculation = chartViewEntityMapper(totalCirculation))
    }
}

private suspend fun loadMarketPrice() {
    val marketPrice = bitcoinChartsRepository.getMarketPrice(27, TimeUnit.DAYS)
    mutex.withLock {
        _viewState.value = _viewState.value.copy(marketPrice = chartViewEntityMapper(marketPrice))
    }
}

可能首选使用主线程或互斥体,因为如果我们创建自己的线程,我们需要确保在不再需要它时正确停止/关闭它。

您可以从官方文档中阅读这篇文章的更多内容:https://kotlinlang.org/docs/shared-mutable-state-and-concurrency.html

【讨论】:

  • 谢谢!我现在使用了互斥锁解决方案,但也尝试了您通过使用 Main 调度程序上下文建议的其他方法。来看看这两种方法的优缺点。
  • 这其实是一个很好的问题:什么是利弊。我没有足够的信心来完全回答它,但我的想法是:我通常避免使用互斥锁,因为它们会产生更复杂且可读性更低的代码。任务队列对我来说感觉更自然,所以:actor 模型、事件循环或单线程执行器——它们都以类似的方式工作。在这种特定情况下,我会说互斥锁是要走的路。互斥正是它的设计目的。而且它不依赖任何外部机制,也没有任何副作用。其他解决方案需要一些外部支持。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多