launch内部调用的每个函数(Dispatchers.IO)是否也在IO Dispatcher中调用



目前,我正试图通过改进不同Dispatcher和上下文的使用来优化我的应用程序性能。我偶然发现的一个问题是,如果我在带有IO调度器的协同程序中启动一个挂起函数,那么其他所有函数也会在同一个调度器中执行吗?

示例

fun doSomething() {
viewModelScope.launch(Dispatchers.IO) {
getUserData(viewModelScope)
}
}
fun getUserData(innerScope: CoroutineScope) {
workerList.startUserDataWorker()
observeUserData(innerScope) // suspend function, is this called inside the IO Dipatcher?
}
// Will this be called inside the IO Dispatcher?
private suspend fun observeUserData(innerScope: CoroutineScope) {
observerWorkerStateAndPassData(workerList.userDataWorkInfo, USER_DATA_OUTPUT_OPTION).collect { status ->
when(status) {
is Status.Loading -> {
_userDataState.postValue(Status.loading())
}
is Status.Success -> {
// Will getShippingAddressList() also be called on the IO Dispatcher?
_userDataState.postValue(Status.success(getShippingAddressList()))
}
is Status.Failure -> {
_userDataState.postValue(Status.failed(status.message.toString()))
}
}
}
}
// Getting Address from the local room cache. Is this called on the IO Dispatcher?
private suspend fun getShippingAddressList(): List<UserDeliveryAddress> {
val uncachedList = userAddressDao.getAllAddress(UserAddressCacheOrder.SHIPPING)
return userAddressCacheMapper.mapFromEntityList(uncachedList)
}

除了以下例外,在调用挂起函数时,您使用的调度程序是不相关的。它仅在调用阻塞函数时才相关。挂起不使用调度程序线程。

例外情况:

  • 您的suspend函数设计不正确,实际上会阻塞。这打破了聚会的惯例。挂起函数不应阻塞
  • 也许你的suspend函数不会阻塞,但它会触及只允许在特定线程上使用的对象,例如Android上的大多数View类。对于这种情况,没有正式的约定——您可以跳过用withContext(Dispatchers.Main)包装函数的内容,从而将仅限main的限制从视图类传递给您的suspend函数调用方。IMO,因为使用withContext可以从一开始就避免潜在的问题,所以您也可以,至少当suspend函数是公共的时是这样。您可以使用Dispatchers.Main.immediate来避免不必要的线程切换
  • 如果您在多个同时协同程序中处理对象,则会产生并发性影响。例如,如果您只使用Main或单个线程分配器触摸特定对象,则不必担心多个线程同时触摸它。我认为,为了进行适当的封装,您应该始终使用withContext(mySingleThreadDispatcher)包装关注对象的这些用法,这样哪个调度器调用您的suspend函数仍然无关紧要

在您的示例中,调度程序调用observeUserData并不重要,因为函数在收集时将无限期挂起。当它收集时,它只调用非阻塞、线程安全的函数LiveData.postValue()

最新更新