协程线程执行问题:ViewPager2滑动多条目后Dispatchers.IO任务不执行如何解决?
你遇到的是Kotlin协程默认Dispatchers.IO并行度耗尽导致的任务饥饿问题。默认Dispatchers.IO最大并行度为64,当所有线程都被长时间占用/阻塞时,后续提交的任务只能进入队列永远得不到执行。
1. 确保非必要任务自动取消
ViewPager2默认仅缓存相邻1-2个Fragment,超出缓存范围的Fragment会被销毁,你在onStart/onResume中启动的lifecycleScope协程默认会在对应生命周期结束时自动取消,需满足两个前提:
- 你调用的suspend函数是可取消的,内部没有忽略
CancellationException - 不要在suspend函数内部用
GlobalScope或者未关联生命周期的其他Scope启动子协程
检查startVideoCaching、sendAnalytics、markVideoView内部逻辑:如果有阻塞IO操作(比如直接调用Java IO流读写、阻塞网络调用),必须用withContext(Dispatchers.IO)包裹,且确保收到取消信号时能终止执行。
2. 隔离视频缓存任务的调度器
你无需移除缓存逻辑,单独为缓存任务创建独立的调度器,不要和API请求共用默认Dispatchers.IO,避免缓存任务占满公共线程池:
// 全局创建独立的缓存调度器,并行度按需设置,比如同时最多4个缓存任务 private val CacheDispatcher = Dispatchers.IO.limitedParallelism(4) // 启动缓存时用这个独立调度器 lifecycleScope.launch(CacheDispatcher) { try { startVideoCaching() } catch (e: Exception) { e.printStackTrace() } }
这样缓存任务最多占用4个IO线程,不会挤掉API请求的执行资源。
3. 修正协程写法规范
ViewModelScope默认调度器是Dispatchers.Main.immediate,不需要指定Dispatchers.IO来启动,直接在内部用withContext切换IO即可:
viewModelScope.launch { val response = withContext(Dispatchers.IO) { repository.call() } when (response) { is Resource.Success -> livedata.value = response.data is Resource.Error -> livedata.value = response.error } }
同时确认repository的call方法是真·挂起函数,没有内部阻塞线程的逻辑。
4. 排查阻塞代码增加超时机制
如果修改后问题仍存在,抓取线程栈确认Dispatchers.IO的线程卡位点:大概率是你认为的suspend函数内部有阻塞调用,比如:
- 直接调用
Thread.sleep()而不是delay() - 使用了未适配挂起的阻塞网络/IO库
- 加了无超时的同步锁、甚至出现死锁
给所有长时间运行的IO操作加超时机制:
lifecycleScope.launch(CacheDispatcher) { // 缓存任务30秒超时自动取消,避免一直占着线程 withTimeoutOrNull(30000) { startVideoCaching() } }
内容的提问来源于stack exchange,提问作者Chhatrasal Singh Bundela

