【问题标题】:IDE warns from a blocking call within a Dispatchers.IO coroutineIDE 从 Dispatchers.IO 协程中的阻塞调用发出警告
【发布时间】:2021-02-04 10:39:13
【问题描述】:

我正在开发一个需要来自 URL 调用的数据的简单应用程序。 以下是相关代码部分

class NewsFeed : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_news_feed)

    lifecycleScope.launchWhenCreated {
        getNewsData()
    }


private suspend fun getNewsData() {
    withContext(Dispatchers.IO) {
        try {
            val jObj = Jsoup.connect(getString(R.string.news_feed)).get()
          //many non relevant lines
BitmapFactory.decodeByteArray(URL(img).readBytes(), 0, 0)

IDE 对 .get() 和 URL() 方法都发出警告,称这是“不适当的线程阻塞方法调用”

但是,如果我理解正确,我在 Dispacher.IO 协程范围内,并且该工作正在为阻塞任务创建的不同线程上完成。 即使我的调用超时,应用程序也不会破坏或阻止 UI。

除了抑制警告,我做错了什么?

【问题讨论】:

  • 协程在这件事上有点奇怪,如果你仔细看看withContext,该方法会将新的调度程序与之前的上下文相加,所以你所拥有的是Main加上@ 987654324@,因为在launchWhenCreated 里面,所以从那里会得到以前的上下文。不是解决方案,但希望能给你一些线索。
  • 感谢您的提问,很久以前我就有同样的问题了。据我记得,在 AS 3.6 中,当调用被包裹在 Dispatchers.IO 中时,警告就会消失。此外,根据我使用分析器的经验,实际方法是在Dispatchers.Main 之外执行的。您可以根据自己的情况自行验证。
  • @cutiko 更改我理解的对 lifeCycle.lunch(Dispachers.IO) 的调用会将 main + IO 上下文更改为仅 IO 并不能解决问题。
  • @SiddharthKamaria 它不会阻塞主线程我可以告诉你那么多,我不明白如何验证。
  • @error86 这也是我的经验,这就是我上面所说的。我的意思是在主要上下文之外,很抱歉造成误解:)

标签: kotlin kotlin-coroutines


【解决方案1】:

你的做法是对的,这只是检测工具的问题。

Dispatchers.IO 正是为这些情况而设计的,因为它分配了额外的线程来跟上并发阻塞 IO 操作。

这里的一位 kotlin 开发人员也解释了这一点: https://medium.com/@elizarov/blocking-threads-suspending-coroutines-d33e11bf4761

特别是“Blocking IO to suspending”段落建议使用

withContext(Dispatchers.IO){
   ...
}

对于阻塞 IO,例如网络调用或读取文件

【讨论】:

    【解决方案2】:

    你是对的,Job 是在不同的线程上完成的,但 Thread 是由与协同程序一起使用的 Dispatcher 管理的。

    如果您在该调度程序中阻塞 Thread,则意味着在您阻塞时线程不能再用于协程。

    您应该使用那些可以暂停的方法的暂停变体,以防止线程忙于等待。

    如果没有挂起替代方案,您最好使用常规 Java 线程池或异步调用,例如 CompletalbeFuture.supplyAsync,因为它们更适合常规阻塞代码。

    【讨论】:

    • 我没有遵循最后一部分,在 Dispacher.IO 上创建的线程旨在处理 IO 任务,因此它们应该被阻塞。如果我错了,请纠正我 Dispacher IO 是一个用于 IO 任务的线程池,对吧?
    • 可以,但不适用于非挂起 IO!
    • 这里只是一点提示:IO 不是忙等待。仅仅因为线程被阻塞,并不意味着它正在消耗 CPU 资源
    • 我认为您根本不需要使用可完成的未来。 Dispatchers.IO 有 64 个线程可用。如果他们希望您运行所有阻止挂起函数的东西,那么将线程数增加到任何超过 Dispatchers.Main 的东西都是无稽之谈。它有更多可用的线程,因为预计一些 IO 将以阻塞方式完成。如果您仍然没有足够的资源,您可以制作另一个具有更大线程池的 Dispatcher。但由于我们似乎使用的是 Android,我相信您可能应该考虑一下为什么您的应用首先需要阻止 >64 个线程。
    • 这不是重点。您只应该使用任何Dispatcher 来进行可以suspend 的计算。如果不能,它将阻止其他 suspending 功能的进行。这就是您收到警告的原因,因为您是在阻止而不是暂停。
    猜你喜欢
    • 1970-01-01
    • 2020-10-04
    • 2019-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-27
    • 1970-01-01
    相关资源
    最近更新 更多