协程有趣行为的解释
Explanation of intriguing behavior of coroutines
我有一个方法可以连续 运行s 一些操作。 Is 实际上是一个 for 循环,它将其内容循环 50 次,每次迭代大约需要 0.2 秒。在此执行期间,我在屏幕上显示了一个恒定的动画。所以,很明显我希望将这些操作从主线程中移除,这样我的动画就可以跟上(或者可以进行重组,这就是 Compose)。我意识到,这个简单的方法
fun run(){
repeat(10000) {
repeat(5000){
print("I ♥ Kotlin")
}
}
}
如果 运行 在标准的可组合范围内,就像人们期望的那样,将阻塞 UI 线程。
b) 如果我在 LaunchedEffect
中调用它,同时将它嵌套在对 launch{...}
.
的调用中,它也会阻塞 UI 线程
c) 如果我在 I/O 协程上 运行 它不会阻塞,这也是默认协程。
d) 如果 运行 在 Main Dispatcher
上,应用程序有时会崩溃
现在,简单的问题 - 这是为什么?
LaunchedEffect(Unit){
run() // Block
}
Launchedeffect(Unit){
launch{
run() // Block
}
}
LaunchedEffect(Unit){
withContext(Dispatchers.Main){
run() //Blocks, and at times, crashes
}
}
LaunchedEffect(Unit){
withContext(Dispatchers.IO){
run() // Runs without blocking
}
}
thread{
run() //Runs without blocking, no crash
}
谁能解释为什么 Dispatchers.IO
有效而其他无效?这有点给我带来不必要的压力。
如果有人需要快速动画UI来测试它,就在这里
@Composable
fun DUM_E_MARK_II() {
val sizeTransition = rememberInfiniteTransition()
val size by sizeTransition.animateFloat(
initialValue = 50f,
targetValue = 200f,
animationSpec = infiniteRepeatable(
keyframes { durationMillis = 1000 },
repeatMode = RepeatMode.Reverse,
)
)
Icon(
imageVector = Icons.Filled.Warning,
contentDescription = "",
modifier = Modifier.size(size.dp),
tint = Color.Red
)
}
您的代码是 long-running、non-suspendable 任务。它会在整个生命周期内阻塞它运行的任何线程。当您阻塞 UI 线程时,它会导致 UI 冻结,并在超时后 Android 终止此类行为不当的应用程序。
如果您使用任何使用自己的线程池的调度程序,例如 IO,任务将阻塞 non-UI 个线程。
withContext(Dispatchers.IO){
run() // Runs without blocking
}
在这里,你明确表示你想在另一个线程上 运行 这个,特别是一个不会对主线程产生影响的线程,所以当你调用:
withContext(Dispatchers.Main){
run() //Blocks, and at times, crashes
}
那么是的,这可能应该崩溃并出现ANR异常,因为主线程被阻塞的时间太长了,那就是the point of withContext指定哪里这项工作应该完成,密集或长时间 运行ning 任务不应在 Dispatchers.Main
This function uses dispatcher from the new context, shifting execution of the block into the different thread if a new dispatcher is specified, and back to the original dispatcher when it completes.
run()
函数是一个long-running函数,它会阻塞执行它的线程。
让我们一一考虑每个案例:
run()
函数在 Main(UI) 线程 中调用,阻塞它。
LaunchedEffect(Unit) {
run() // Block
}
run()
在协程内部调用,协程使用 launch
协程构建器启动。协程的上下文是组合的 CoroutineContext
,我假设它由 Dispatchers.Main
调度程序组成。所以run
函数也在Main(UI)线程中调用,阻塞了
Launchedeffect(Unit) {
launch {
run() // Block
}
}
您可以使用 withContext(Dispatchers.IO)
使 run()
函数 suspend
,它会将 run
函数的执行上下文切换到 Dispatchers.IO
线程池:
suspend fun run() = withContext(Dispatchers.IO) {
// this is executed in background thread
}
Launchedeffect(Unit) {
run() // Not Blocking
}
Launchedeffect(Unit) {
launch {
run() // Not Blocking
}
}
run()
函数在 Main(UI) thread 中调用,阻塞它,因为使用了 Dispatchers.Main
用于其上下文执行。 Dispatchers.Main
在 Main(UI) 线程.
中执行协程
LaunchedEffect(Unit){
withContext(Dispatchers.Main){
run() // Blocks, and at times, crashes
}
}
在这种情况下它运行时不会阻塞,因为 Dispatchers.IO
被用作协程上下文。它使用后台线程池。它不会阻塞主线程,因为它在后台线程中执行。
LaunchedEffect(Unit){
withContext(Dispatchers.IO){
run() // Runs without blocking
}
}
这会在不阻塞主线程的情况下运行,因为另一个线程(后台线程)用于执行它。
thread{
run() //Runs without blocking, no crash
}
我有一个方法可以连续 运行s 一些操作。 Is 实际上是一个 for 循环,它将其内容循环 50 次,每次迭代大约需要 0.2 秒。在此执行期间,我在屏幕上显示了一个恒定的动画。所以,很明显我希望将这些操作从主线程中移除,这样我的动画就可以跟上(或者可以进行重组,这就是 Compose)。我意识到,这个简单的方法
fun run(){
repeat(10000) {
repeat(5000){
print("I ♥ Kotlin")
}
}
}
如果 运行 在标准的可组合范围内,就像人们期望的那样,将阻塞 UI 线程。
b) 如果我在 LaunchedEffect
中调用它,同时将它嵌套在对 launch{...}
.
c) 如果我在 I/O 协程上 运行 它不会阻塞,这也是默认协程。
d) 如果 运行 在 Main Dispatcher
上,应用程序有时会崩溃现在,简单的问题 - 这是为什么?
LaunchedEffect(Unit){
run() // Block
}
Launchedeffect(Unit){
launch{
run() // Block
}
}
LaunchedEffect(Unit){
withContext(Dispatchers.Main){
run() //Blocks, and at times, crashes
}
}
LaunchedEffect(Unit){
withContext(Dispatchers.IO){
run() // Runs without blocking
}
}
thread{
run() //Runs without blocking, no crash
}
谁能解释为什么 Dispatchers.IO
有效而其他无效?这有点给我带来不必要的压力。
如果有人需要快速动画UI来测试它,就在这里
@Composable
fun DUM_E_MARK_II() {
val sizeTransition = rememberInfiniteTransition()
val size by sizeTransition.animateFloat(
initialValue = 50f,
targetValue = 200f,
animationSpec = infiniteRepeatable(
keyframes { durationMillis = 1000 },
repeatMode = RepeatMode.Reverse,
)
)
Icon(
imageVector = Icons.Filled.Warning,
contentDescription = "",
modifier = Modifier.size(size.dp),
tint = Color.Red
)
}
您的代码是 long-running、non-suspendable 任务。它会在整个生命周期内阻塞它运行的任何线程。当您阻塞 UI 线程时,它会导致 UI 冻结,并在超时后 Android 终止此类行为不当的应用程序。
如果您使用任何使用自己的线程池的调度程序,例如 IO,任务将阻塞 non-UI 个线程。
withContext(Dispatchers.IO){
run() // Runs without blocking
}
在这里,你明确表示你想在另一个线程上 运行 这个,特别是一个不会对主线程产生影响的线程,所以当你调用:
withContext(Dispatchers.Main){
run() //Blocks, and at times, crashes
}
那么是的,这可能应该崩溃并出现ANR异常,因为主线程被阻塞的时间太长了,那就是the point of withContext指定哪里这项工作应该完成,密集或长时间 运行ning 任务不应在 Dispatchers.Main
This function uses dispatcher from the new context, shifting execution of the block into the different thread if a new dispatcher is specified, and back to the original dispatcher when it completes.
run()
函数是一个long-running函数,它会阻塞执行它的线程。
让我们一一考虑每个案例:
run()
函数在 Main(UI) 线程 中调用,阻塞它。LaunchedEffect(Unit) { run() // Block }
run()
在协程内部调用,协程使用launch
协程构建器启动。协程的上下文是组合的CoroutineContext
,我假设它由Dispatchers.Main
调度程序组成。所以run
函数也在Main(UI)线程中调用,阻塞了Launchedeffect(Unit) { launch { run() // Block } }
您可以使用 withContext(Dispatchers.IO)
使 run()
函数 suspend
,它会将 run
函数的执行上下文切换到 Dispatchers.IO
线程池:
suspend fun run() = withContext(Dispatchers.IO) {
// this is executed in background thread
}
Launchedeffect(Unit) {
run() // Not Blocking
}
Launchedeffect(Unit) {
launch {
run() // Not Blocking
}
}
中执行协程run()
函数在 Main(UI) thread 中调用,阻塞它,因为使用了Dispatchers.Main
用于其上下文执行。Dispatchers.Main
在 Main(UI) 线程.LaunchedEffect(Unit){ withContext(Dispatchers.Main){ run() // Blocks, and at times, crashes } }
在这种情况下它运行时不会阻塞,因为
Dispatchers.IO
被用作协程上下文。它使用后台线程池。它不会阻塞主线程,因为它在后台线程中执行。LaunchedEffect(Unit){ withContext(Dispatchers.IO){ run() // Runs without blocking } }
这会在不阻塞主线程的情况下运行,因为另一个线程(后台线程)用于执行它。
thread{ run() //Runs without blocking, no crash }