Asynchronous Kotlin-协同程序范围,为什么不';我的异步命令不能执行吗?
合作计划是如何运作的 假设我有一个Asynchronous Kotlin-协同程序范围,为什么不';我的异步命令不能执行吗?,asynchronous,kotlin,actor,channel,kotlinx.coroutines,Asynchronous,Kotlin,Actor,Channel,Kotlinx.coroutines,合作计划是如何运作的 假设我有一个 enum class ConceptualPosition{ INVALID, A,B } 假设我有一个用户界面,用户可以从中单击任意位置,a或B 我现在想要一个参与者,它接收用户输入,但在实际请求输入之前忽略它。为了简单起见,假设只有一种方式可以申请职位 sealed class PositionRequest{ /**report the next position offered*/ object ForwardNext
enum class ConceptualPosition{
INVALID,
A,B
}
假设我有一个用户界面,用户可以从中单击任意位置,a
或B
我现在想要一个参与者,它接收用户输入,但在实际请求输入之前忽略它。为了简单起见,假设只有一种方式可以申请职位
sealed class PositionRequest{
/**report the next position offered*/
object ForwardNext:PositionRequest()
}
import ConceptualPosition.*
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
fun main(args: Array<String>) = runBlocking{
val ui = BasicUI()
println("actor engaged")
//these should all be ignored
repeat(5){ui.offerPosition(A)}
println("offered some 'A's")
//keep offering 'B' so that eventually, one will be offered after we request a position
async { while(true){ui.offerPosition(B)} }
//now get a 'B'
println("requesting a position")
val pos = ui.getPosition()
println("received '$pos'")
}
所以我们可以这样构造:
fun CoroutineScope.positionActor(
offeredPosition:ReceiveChannel<ConceptualPosition>,
requests:ReceiveChannel<PositionRequest>,
output:SendChannel<ConceptualPosition>
) = launch{
var lastReceivedPosition = INVALID
var forwardNextReceived = 0
println("ACTOR: entering while loop")
while(true) {
select<Unit> {
requests.onReceive {
println("ACTOR: requests.onReceive($it)")
when (it) {
is PositionRequest.ForwardNext -> ++forwardNextReceived
}
}
offeredPosition.onReceive {
println("ACTOR: offeredPosition.onReceive($it)")
lastReceivedPosition = it
if (forwardNextReceived > 0) {
--forwardNextReceived
output.send(it)
}
}
}
}
}
让我们构建一个小测试用例:我将为演员提供一些他应该忽略的a
s,然后启动一个持续提供B
s的协同程序,当我向演员请求职位时,其中一个将返回给我
sealed class PositionRequest{
/**report the next position offered*/
object ForwardNext:PositionRequest()
}
import ConceptualPosition.*
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
fun main(args: Array<String>) = runBlocking{
val ui = BasicUI()
println("actor engaged")
//these should all be ignored
repeat(5){ui.offerPosition(A)}
println("offered some 'A's")
//keep offering 'B' so that eventually, one will be offered after we request a position
async { while(true){ui.offerPosition(B)} }
//now get a 'B'
println("requesting a position")
val pos = ui.getPosition()
println("received '$pos'")
}
。。。什么也没有
显然,B
从来没有被提供过,因此也从来没有被转发过,这会导致主线程阻塞(在这种情况下应该是这样的)
我扔了一个硬币
if(conceptualPosition == ConceptualPosition.B) throw RuntimeException("B offered?!")
进入BasicUI.offerPosition
,也没有例外,所以
在这一点上,我可能不得不承认我还不了解KotlinCoroutineScope
为什么这个例子不起作用?这里似乎有两个问题:
offerPosition
/getPosition
不是挂起函数。在大多数情况下,使用runBlocking
是错误的解决方案,在必须与同步代码或主功能接口时应使用CoroutineScope
中执行不带任何参数的async
。对于您的主要功能,这是运行阻塞
。文档实际上描述了以下行为:async
块在其他continuation使用它时不会在事件循环中执行。因为getPosition
正在阻止事件循环
将阻塞函数替换为挂起函数,并使用上下文(dispatcher)在不同的执行器上进行调度,将允许异步函数运行并最终解决状态。这里似乎有两个问题:
offerPosition
/getPosition
不是挂起函数。在大多数情况下,使用runBlocking
是错误的解决方案,在必须与同步代码或主功能接口时应使用CoroutineScope
中执行不带任何参数的async
。对于您的主要功能,这是运行阻塞
。文档实际上描述了以下行为:async
块在其他continuation使用它时不会在事件循环中执行。因为getPosition
正在阻止事件循环
将阻塞函数替换为挂起函数,并使用上下文(dispatcher)在不同的执行器上进行调度,将允许异步函数运行并最终解决状态。是否有一种解决方案可以避免将
offerPosition
/getPosition
挂起?我的意思是,拥有门面的全部意义在于,客户不需要知道涉及到协同程序;从他的观点来看,他只是调用一个返回概念位置的方法。通过阻塞同步返回值的函数不会与co例程混合使用。您需要使它们挂起
,或者返回一个未来而不是一个值(延迟
或类似的内容)。在当前状态下,您根本无法从使用co例程中获益。是否有一种解决方案可以避免将offerPosition
/getPosition
挂起?我的意思是,拥有门面的全部意义在于,客户不需要知道涉及到协同程序;从他的观点来看,他只是调用一个返回概念位置的方法。通过阻塞同步返回值的函数不会与co例程混合使用。您需要使它们挂起
,或者返回一个未来而不是一个值(延迟
或类似的内容)。在当前状态下,您根本无法从使用co例程中获益。
if(conceptualPosition == ConceptualPosition.B) throw RuntimeException("B offered?!")