按照您设置问题的方式,您不应期望从协程中获得任何好处。在所有情况下,您都会向执行器提交不可分割的计算块。您没有利用协程挂起的想法,您可以在其中编写实际上被切碎并分段执行的顺序代码,可能在不同的线程上。
协程的大多数用例都围绕阻塞代码:避免占用线程只等待响应而不执行任何操作的情况。它们还可以用于交错 CPU 密集型任务,但这是一种更特殊的情况。
我建议对涉及多个连续阻塞步骤的 1,000,000 个任务进行基准测试,例如Roman Elizarov 在 KotlinConf 2017 上的演讲 https://youtu.be/_hfBv0a09Jc?t=10m12s:
suspend fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
其中所有的requestToken()
, createPost()
and processPost()
涉及网络调用。
如果您有两种实现方式,其中一种是suspend fun
s 和另一个具有常规阻塞函数的函数,例如:
fun requestToken() {
Thread.sleep(1000)
return "token"
}
vs.
suspend fun requestToken() {
delay(1000)
return "token"
}
您会发现您甚至无法设置执行第一个版本的 1,000,000 个并发调用,并且如果您将数量降低到无需OutOfMemoryException: unable to create new native thread
,协程的性能优势应该是显而易见的。
如果您想探索协程对于 CPU 密集型任务的可能优势,则需要一个用例,其中顺序执行还是并行执行它们都不是无关紧要的。在上面的示例中,这被视为不相关的内部细节:在一个版本中,您运行 1,000 个并发任务,而在另一个版本中,您仅使用 4 个任务,因此几乎是顺序执行。
黑泽尔卡斯特喷射机 https://jet-start.sh/docs/architecture/execution-engine是此类用例的一个示例,因为计算任务是相互依赖的:一个的输出是另一个的输入。在这种情况下,您不能只运行其中的几个直到完成,在一个小线程池上,您实际上必须交错它们,以便缓冲的输出不会爆炸。如果您尝试使用或不使用协程来设置这样的场景,您将再次发现您要么分配与任务一样多的线程,要么使用可挂起的协程,而后一种方法会获胜。 Hazelcast Jet 在纯 Java API 中实现了协程的精神。它的方法将极大地受益于协程编程模型,但目前它是纯 Java 的。
Disclosure: the author of this post belongs to the Jet engineering team.