为什么这个协程阻止UI线程?

vog*_*Dev 4 multithreading android kotlin kotlin-coroutines

我发现我的Android App中的这个特定代码阻止了UI线程:

runBlocking {
    async(CommonPool) {
        Thread.sleep(5000)     
    }.await()
}

textView.text = "Finish!"
Run Code Online (Sandbox Code Playgroud)

我一直在使用协同程序执行多个任务,并且它们从不阻止UI线程,可以在文档中阅读:

.协同程序提供了一种避免阻塞线程并用更便宜和更可控的操作替换它的方法:协同程序的暂停

但奇怪的是,这段代码:

runBlocking {
    async(CommonPool) {
        launch(CommonPool) {
            Thread.sleep(5000)

            runOnUiThread { textView.text = "Finish!" }
        }
    }.await()
}
Run Code Online (Sandbox Code Playgroud)

表现如预期; 不阻塞,等待五秒然后打印结果(我需要更新UI后,只有在sleep完成后)

文档说明async并且launch可以独立使用,不需要组合.其实async(CommonPool)应该够了.

那么这里到底发生了什么?为什么它只适用于async+launch

更新

我的完整示例代码:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_main)

        button1.setOnClickListener {
            runBlocking {
                async(CommonPool) {
                    Thread.sleep(5000L)

                }.await()
            }

            textView1.text = "Finally! I've been blocked for 5s :-("
        }

        button2.setOnClickListener {
            runBlocking {
                async(CommonPool) {
                    launch(CommonPool) {
                        Thread.sleep(5000L)

                        runOnUiThread { textView1.text = "Done! UI was not blocked :-)" }
                    }
                }.await()
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*nik 6

runBlocking不是在UI线程上启动协同程序的方法,因为正如其名称所示,它将阻止托管线程,直到协程完成.您必须launchUI上下文中,然后切换到CommonPool重量级操作的上下文.而且,当我们在它的时候,你应该放弃它async-await并使用withContext:

button1.setOnClickListener {
    launch(UI) {
        withContext(CommonPool) {
            Thread.sleep(5000L)
        }
        textView1.text = "Done! UI was not blocked :-)"
    }
}
Run Code Online (Sandbox Code Playgroud)

withContext将挂起协程直到完成,然后在父上下文中恢复它,这是UI.

关于CommonPool的使用

尽管使用它非常简单CommonPool,但这意味着您要提交到所有依赖项都可以使用的全局池,而您无法控制它.我建议的是创建自己的线程池,还可以根据需要配置它.代码非常简单:

val threadPool = Executors.newCachedThreadPool().asCoroutineDispatcher()
Run Code Online (Sandbox Code Playgroud)

然后你只是说withContext(threadPool)而不是withContext(CommonPool).