Kotlin 协程 lockup/freezing
Kotlin Coroutines lockup/freezing
我在 运行 宁协程时遇到奇怪的锁定(冻结)。有时它工作得很好,有时它会等待并在很长时间后继续 运行ning(或根本不)。我不知道如果我的设置不正确,存在一些竞争条件,或者我可能误解了协程应该如何使用。应用程序本身不会冻结或崩溃,它只是协程 stop/pause 执行。
例如,在这一点中 scope.launch
有时不会继续执行到 billingClientDeferred.await()
override suspend fun getPurchases(): Result<List<Purchase>> = suspendCoroutine { continuation ->
scope.launch {
billingClientDeferred.await().success { client ->
client.queryPurchasesAsync(BillingClient.SkuType.SUBS) Subs@{ billingResultSubs, purchasesSubs ->
if (billingResultSubs.responseCode != BillingClient.BillingResponseCode.OK) {
continuation.resume(Result.Failure.Unknown())
return@Subs
}
continuation.resume(Result.Success(purchasesSubs))
}
}
}
}
scope
声明为:
private val scope = CoroutineScope(Job() + Dispatchers.Default)
然后从WalletManager
调用
override suspend fun verifyProducts(): Result<Unit> = billingProvider.getPurchases()
.successSuspend { purchases ->
purchases.forEach {
activatePurchase(it)
}
}.map { }
最后是从这样的ViewModel
中调用的
override fun verify() {
viewModelScope.launch {
userSupervisor.walletManager.mapResult {
it.verifyProducts()
}
}
}
我猜我使用的调度程序和范围的组合存在一些问题。我正在尝试使用 Dispatchers.Default
、Dispatchers.Main
、使 class 符合 CoroutineScope
的不同方法,使用全局范围,但我总是 运行 进入某些 threading/lockup我不完全理解的问题。
第一个问题是您不想从 suspendCoroutine
中启动新的协程,您需要重组代码以便不必执行此操作。这可以作为
private fun someMethod() = viewModelScope.launch {
val client = billingClientDeferred.await()
val finalResult = getPurchases(client)
}
在此之后只需更新 getPurchases
以将客户端作为参数排除并从中删除所有协程代码,只需调用客户端并 return 使用延续的结果。
您的代码的根本问题是您没有遵循 structured concurrency
的原则。
它本质上是一种并发管理模式,隐式地处理协程的范围和生命周期。这个想法是,当您使用不同的范围启动协程时,这些范围必须具有父子关系,这允许协程管理系统处理所有情况,例如
- 当内部协程抛出异常时会发生什么
- 当外部协程被取消或失败时会发生什么
恐怕您的代码并没有真正遵循这种模式并且存在以下问题
scope
与启动 getPurchases
的 CoroutineScope
没有任何关系
这是个大问题,取消outer scope会怎样,是取消inner coroutine还是inner coroutine会一直消耗资源?
您正在从挂起的函数中启动协程
正如 Roman Elizarov 解释的那样 here
Suspending functions, on the other hand, are designed to be
non-blocking and should not have side-effects of launching any
concurrent work. Suspending functions can and should wait for all
their work to complete before returning to the caller.
我在 运行 宁协程时遇到奇怪的锁定(冻结)。有时它工作得很好,有时它会等待并在很长时间后继续 运行ning(或根本不)。我不知道如果我的设置不正确,存在一些竞争条件,或者我可能误解了协程应该如何使用。应用程序本身不会冻结或崩溃,它只是协程 stop/pause 执行。
例如,在这一点中 scope.launch
有时不会继续执行到 billingClientDeferred.await()
override suspend fun getPurchases(): Result<List<Purchase>> = suspendCoroutine { continuation ->
scope.launch {
billingClientDeferred.await().success { client ->
client.queryPurchasesAsync(BillingClient.SkuType.SUBS) Subs@{ billingResultSubs, purchasesSubs ->
if (billingResultSubs.responseCode != BillingClient.BillingResponseCode.OK) {
continuation.resume(Result.Failure.Unknown())
return@Subs
}
continuation.resume(Result.Success(purchasesSubs))
}
}
}
}
scope
声明为:
private val scope = CoroutineScope(Job() + Dispatchers.Default)
然后从WalletManager
override suspend fun verifyProducts(): Result<Unit> = billingProvider.getPurchases()
.successSuspend { purchases ->
purchases.forEach {
activatePurchase(it)
}
}.map { }
最后是从这样的ViewModel
中调用的
override fun verify() {
viewModelScope.launch {
userSupervisor.walletManager.mapResult {
it.verifyProducts()
}
}
}
我猜我使用的调度程序和范围的组合存在一些问题。我正在尝试使用 Dispatchers.Default
、Dispatchers.Main
、使 class 符合 CoroutineScope
的不同方法,使用全局范围,但我总是 运行 进入某些 threading/lockup我不完全理解的问题。
第一个问题是您不想从 suspendCoroutine
中启动新的协程,您需要重组代码以便不必执行此操作。这可以作为
private fun someMethod() = viewModelScope.launch {
val client = billingClientDeferred.await()
val finalResult = getPurchases(client)
}
在此之后只需更新 getPurchases
以将客户端作为参数排除并从中删除所有协程代码,只需调用客户端并 return 使用延续的结果。
您的代码的根本问题是您没有遵循 structured concurrency
的原则。
它本质上是一种并发管理模式,隐式地处理协程的范围和生命周期。这个想法是,当您使用不同的范围启动协程时,这些范围必须具有父子关系,这允许协程管理系统处理所有情况,例如
- 当内部协程抛出异常时会发生什么
- 当外部协程被取消或失败时会发生什么
恐怕您的代码并没有真正遵循这种模式并且存在以下问题
scope
与启动 getPurchases
的 CoroutineScope
没有任何关系
这是个大问题,取消outer scope会怎样,是取消inner coroutine还是inner coroutine会一直消耗资源?
您正在从挂起的函数中启动协程
正如 Roman Elizarov 解释的那样 here
Suspending functions, on the other hand, are designed to be non-blocking and should not have side-effects of launching any concurrent work. Suspending functions can and should wait for all their work to complete before returning to the caller.