在异步编程的世界里,协程以其轻量级、高效能的特性成为了无数开发者的心头好。但你知道吗?即使是看似简单的协程任务,背后也可能隐藏着复杂的线程逻辑。今天,就让我们一起揭开协程中线程的神秘面纱。
在协程的内部,其实有一个隐形的线程池在默默地工作。这个线程池不像操作系统层面的线程池那样受限于硬件资源,它可以根据需要动态地创建和销毁线程。更神奇的是,同一个协程内部,你可以同时运行default任务和io任务,而这两个任务很可能会由同一个线程来执行。
举个例子,假设我们有一个协程任务,它首先被调度到default调度器上执行,然后又被切换到io调度器上执行。由于调度器的调度策略和线程池的内部实现,这两个任务有可能由同一个线程来完成。这就意味着,你在调试代码时,可能会看到相同的线程id在短时间内连续出现。
val set = HashSet<Int>()
viewModelScope.launch(Dispatchers.IO.limitedParallelism(200)) {
repeat(200) {
launch {
e("1 index =$it") set.add(System.identityHashCode(Thread.currentThread()))
Thread.sleep(2000)
}
}
}
viewModelScope.launch(Dispatchers.IO.limitedParallelism(100)) {
repeat(100) {
launch {
e("index =$it") set.add(System.identityHashCode(Thread.currentThread()))
Thread.sleep(2000)
}
}
}
在这个例子中,即使我们将Dispatchers.IO.limitedParallelism
的值设置为200,但由于协程内部的线程池机制,我们仍然可以看到大量的线程被创建和销毁。这就是为什么set.size
的值会远远超过我们预期的原因。
协程的线程池是一个不限量的线程池,这意味着它可以创建任意多的线程。但是,这并不意味着任务的提交速度可以无限制地加快。因为每个任务都需要一定的时间来执行,所以当任务数量过多时,就会出现任务排队的情况。
为了避免这种情况,我们可以使用limitedParallelism
方法来绕过任务数量的限制。这个方法会在将任务提交给线程池之前,先对任务数量做一次限制。这样,每次调用该方法时,都会生成一个新的任务队列,从而避免任务排队的问题。
viewModelScope.launch(Dispatchers.IO.limitedParallelism(200)) {
repeat(200) {
launch {
e("1 index =$it") set.add(System.identityHashCode(Thread.currentThread()))
Thread.sleep(2000)
}
}
}
需要注意的是,limitedParallelism
方法只对IO有效,对default任务无论指定成多大的值都会被协程内部限制。这是因为default任务通常是由一个单独的线程池来处理的,而这个线程池有自己的任务数量限制。
即使在同一个协程块中,挂起前和恢复后的线程也可能不是同一个。这取决于恢复的代码是在哪个线程中调用的。例如,如果我们在一个协程中启动另一个协程,并且这个新协程使用了相同的调度器,那么新协程就有可能运行在另一个线程中。
viewModelScope.launch(Dispatchers.IO) {
repeat(200) {valid = System.identityHashCode(Thread.currentThread())
delay(100)valid2 = System.identityHashCode(Thread.currentThread())if(id != id2){
e("id =$id, id2 =$id2")
}
}
}
在这个例子中,我们看到即使在同一个协程块中,valid
和valid2
变量的值也可能不同。这证明了即使在同一个线程中,协程的挂起和恢复操作也可能会导致线程的不同。
同一个调度器内部再次使用该调度器启动协程,新协程也有可能运行在另一个线程中。这是因为调度器的工作方式决定了它可以在不同的线程之间切换协程的执行。
viewModelScope.launch(Dispatchers.IO) {
repeat(200) {valid = System.identityHashCode(Thread.currentThread())
launch(Dispatchers.IO) {valid2 = System.identityHashCode(Thread.currentThread())if(id != id2) {
e("id =$id, id2 =$id2")
}
}
}
}
在这个例子中,我们看到即使在同一个调度器内部,新启动的协程也有可能运行在不同的线程中。这进一步证明了协程的线程管理机制的复杂性。
协程作为一种轻量级的异步编程模型,虽然看起来简单易用,但其背后的线程逻辑却远比我们想象的要复杂得多。通过深入了解协程的线程池机制、任务排队、线程不确定性以及调度器的奥秘,我们可以更好地掌握协程的使用,从而编写出更加高效、稳定的异步代码。
声明:
1、本博客不从事任何主机及服务器租赁业务,不参与任何交易,也绝非中介。博客内容仅记录博主个人感兴趣的服务器测评结果及一些服务器相关的优惠活动,信息均摘自网络或来自服务商主动提供;所以对本博客提及的内容不作直接、间接、法定、约定的保证,博客内容也不具备任何参考价值及引导作用,访问者需自行甄别。
2、访问本博客请务必遵守有关互联网的相关法律、规定与规则;不能利用本博客所提及的内容从事任何违法、违规操作;否则造成的一切后果由访问者自行承担。
3、未成年人及不能独立承担法律责任的个人及群体请勿访问本博客。
4、一旦您访问本博客,即表示您已经知晓并接受了以上声明通告。
本站资源仅供个人学习交流,请于下载后24小时内删除,不允许用于商业用途,否则法律问题自行承担。
Copyright 2005-2024 yuanmayuan.com 【源码园】 版权所有 备案信息
声明: 本站非腾讯QQ官方网站 所有软件和文章来自互联网 如有异议 请与本站联系 本站为非赢利性网站 不接受任何赞助和广告