当前位置: 首页>后端>正文

Android kotlin 中 runInterruptible 的作用

背景

coil 源码时,发现其内部有使用到 kotlin 的 runInterruptible 这个方法,参考 Android 关于 Coil 源码阅读之部分疑问记录 - 简书 (jianshu.com)。于是乎就在想这个方法到底有什么用,下面用实验来说明。

结论

先说结论,总的来说,就是当内部工作可能阻塞线程时,可以使用 runInterruptible 这个方法包裹工作,然后本次 job 被 cancel 时,可以有效的解除本线程的阻塞状态。大致流程是协程 cancel -> Thread#interrupt(),进而解除阻塞状态。

实验

  1. 创建一个单线程的 CoroutineScope,一个协程里执行阻塞任务,另一个协程任务则无法执行。IDE 也智能地提示了 take 方法可能导致线程饥饿:
    Android kotlin 中 runInterruptible 的作用,第1张
    线程饥饿.png
@OptIn(InternalCoroutinesApi::class)
class ExampleUnitTest {
    @Test
    fun main() {
        runBlocking {
            test()
        }

        // 确保上面代码执行完毕
        Thread.sleep(3000L)
    }

    fun test() {
        val context = ExperimentalCoroutineDispatcher(1, 1, 3000L, "single-thread")
        val scope = CoroutineScope(context)
        val queue = LinkedBlockingQueue<Int>(1)
        val job = scope.launch {
            try {
                // runInterruptible {
                println("阻塞前")
                queue.take()
                // }
            } catch (e: CancellationException) {
                println("eee: ${e.message}")
            }
        }

        scope.launch {
            println("阻塞后")
        }

        /*thread {
            job.cancel("cancel xxx")
        }*/
    }
}

打印日志:

阻塞前

可以看到只打印了 阻塞前 而没有打印 阻塞后,说明线程时被阻塞了的。

  1. 即时调用了 cancel 也无法解除阻塞状态
@OptIn(InternalCoroutinesApi::class)
class ExampleUnitTest {
    @Test
    fun main() {
        runBlocking {
            test()
        }

        // 确保上面代码执行完毕
        Thread.sleep(3000L)
    }

    fun test() {
        val context = ExperimentalCoroutineDispatcher(1, 1, 3000L, "single-thread")
        val scope = CoroutineScope(context)
        val queue = LinkedBlockingQueue<Int>(1)
        val job = scope.launch {
            try {
                // runInterruptible {
                println("阻塞前")
                queue.take()
                // }
            } catch (e: CancellationException) {
                println("eee: ${e.message}")
            }
        }

        scope.launch {
            println("阻塞后")
        }

        thread {
            job.cancel("cancel xxx")
        }
    }
}

打印日志:

阻塞前
  1. 加上 runInterruptible 后,job 被 cancel 时可正常解除阻塞状态了
@OptIn(InternalCoroutinesApi::class)
class ExampleUnitTest {
    @Test
    fun main() {
        runBlocking {
            test()
        }

        // 确保上面代码执行完毕
        Thread.sleep(3000L)
    }

    fun test() {
        val context = ExperimentalCoroutineDispatcher(1, 1, 3000L, "single-thread")
        val scope = CoroutineScope(context)
        val queue = LinkedBlockingQueue<Int>(1)
        val job = scope.launch {
            try {
                runInterruptible {
                    println("阻塞前")
                    queue.take()
                }
            } catch (e: CancellationException) {
                println("eee: ${e.message}")
            }
        }

        scope.launch {
            println("阻塞后")
        }

        thread {
            job.cancel("cancel xxx")
        }
    }
}

打印日志:

阻塞前
eee: cancel xxx
阻塞后

可以看到打印了 阻塞后,证明线程确实解除了阻塞状态。

源码

最后,贴一下源码:

Android kotlin 中 runInterruptible 的作用,第2张
runInterruptible 源码.png

https://www.xamrdz.com/backend/3zn1941855.html

相关文章: