我有一个看起来像这样的函数:
fun <R> map(block: (T) -> R): Result<R> { ... }
,我想制作一个暂停版本:
suspend fun <R> mapAsync(block: suspend (T) -> R): Result<R> { ... }
两个主体中的逻辑相同,但是一个挂起,另一个挂起。
我不想有这种重复的逻辑。我发现要执行此操作的唯一方法是将map
函数调用到mapAsync
函数,然后将结果包装在runBlocking
中:
fun <R> map(block: (T) -> R): Result<R> =
runBlocking { mapAsync { block(it) } }
所以我有两个问题:
suspend
参数传递,然后阻塞直到结果完成,是否有性能方面的考虑?
(T) -> R
,尽管我不知道编译器是否可以告诉您。答案 0 :(得分:3)
您遇到了臭名昭著的“ colored function”问题。这两个世界确实是分开的,尽管您可以添加一个统一它们的表面层,但您不能以零性能成本获得它。这是如此基础,即使假设您的suspend
块实际上从未挂起,并且包装层利用了该假设并且甚至没有在其上使用runBlocking
,您也会静止付出“准备暂停”的代价。不过,价格并不昂贵:这意味着每个suspend fun
调用都会创建一个小对象,以容纳通常驻留在线程的本机调用堆栈中的数据。在您的情况下,只有外部块是可挂起的,所以这只是一个这样的对象。
runBlocking
在您调用它的线程上运行协程,除非协程自身挂起,否则它将在同一线程上同步完成。因此,在suspend
块中有一些同步代码的情况下,线程协调不会带来额外的性能损失。
如果协程确实挂起了自己,那么将必须有一些外部工作线程来对允许协程恢复的事件做出反应,并且该线程与原始{{1 }}线程。这是存在或不存在协程的基本机制。
答案 1 :(得分:1)
您的方法是正确的,runBlocking
经过专门设计,可以用作阻塞操作和非阻塞操作之间的连接。从文档中:
运行新的协程并中断当前线程,直到其 完成。协程不应该使用此功能。 是 旨在将常规阻止代码桥接到编写的库 ,用于悬浮液,用于主要功能和测试。
还有Roman Elizarov的一些有趣的视频: