Kotlin中可靠取消协程或线程的方法(含readln()阻塞场景)
Linux下Kotlin实现Boggle可中断定时器方案
核心问题是JVM的readln()是阻塞式IO,会卡住协程/线程,而且没法被中断。结合Linux环境,给你两个简单可行的方案:
方案一:非阻塞输入检测+协程
利用System.in.available()检查输入缓冲区是否有字符,搭配协程实现无阻塞的按键检测,同时定时器正常运行:
import kotlinx.coroutines.* fun main() = runBlocking { // 启动定时器协程:5秒后提示时间到 val timerJob = launch { delay(5000) println("\n时间到!") print('\u0007') // 播放系统提示音 } // 启动输入检测协程:循环检查按键输入 val inputJob = launch { while (true) { // 检查是否有按键输入(缓冲区有字节) if (System.in.available() > 0) { System.in.read() // 读取按键字符(无需等回车) println("\n提前终止!") timerJob.cancel() // 取消定时器 break } delay(100) // 短暂休眠,避免CPU空转 } } // 等待任一任务完成,结束程序 joinAll(timerJob, inputJob) }
注意:Linux终端默认是「规范模式」,按键会缓存到回车才提交给程序。所以上面的代码在默认终端下,还是需要按回车才能触发中断。要解决这个问题,需要临时修改终端模式。
方案二:修改终端模式+非阻塞检测
通过stty命令修改终端为非规范模式,让按键立即提交,无需等待回车:
import kotlinx.coroutines.* import java.lang.Runtime.getRuntime fun main() = runBlocking { // 修改终端为非规范模式:按键立即提交,无需回车 getRuntime().exec("stty -icanon min 1").waitFor() try { val timerJob = launch { delay(5000) println("\n时间到!") print('\u0007') } val inputJob = launch { while (true) { if (System.in.available() > 0) { System.in.read() println("\n提前终止!") timerJob.cancel() break } delay(100) } } joinAll(timerJob, inputJob) } finally { // 程序结束后必须恢复终端规范模式,否则终端会异常 getRuntime().exec("stty icanon").waitFor() } }
为什么之前的方案失败?
- 协程方案中
readln()是阻塞式IO,会占用整个线程,导致定时器协程无法调度执行; - 线程方案中
Thread.interrupt()无法打断readln()这类IO阻塞操作,而Thread.stop()已废弃且不安全。
内容的提问来源于stack exchange,提问作者ArrayBolt3
相关产品推荐
相关产品推荐

