在Kotlin中,如何在保持结构化并发能力的同时使用参与者?

在Kotlin中,如何在保持结构化并发能力的同时使用参与者?,kotlin,kotlin-coroutines,kotlin-multiplatform,Kotlin,Kotlin Coroutines,Kotlin Multiplatform,我有一个类,它使用参与者来确保共享可变状态的线程安全。我为这个演员做了一个小包装,使其易于使用: interface Ref<T : Any> { fun get(): T fun transform(transformer: (T) -> T): Job } transform在没有运行阻塞的情况下执行类似的操作,只返回一个作业: 在转换调用导致另一个调用之前,这是正常的: ref.transform { ... ref.transform

我有一个类,它使用参与者来确保共享可变状态的线程安全。我为这个演员做了一个小包装,使其易于使用:

interface Ref<T : Any> {

    fun get(): T

    fun transform(transformer: (T) -> T): Job

}
transform在没有运行阻塞的情况下执行类似的操作,只返回一个作业:

在转换调用导致另一个调用之前,这是正常的:

ref.transform {

  ...
  ref.transform {

  }
}
在这里,我有两份工作,但没有办法将它们合并为一份工作,如果我想等待它们完成,我可以调用join

解决这个问题的方法是结构化并发,但是我不知道如何创建我的actor,因为它被定义为CoroutineScope上的一个扩展

如何在保持使用结构化并发的能力的同时继续使用actor

请注意,我之所以创建Ref,是因为我的项目是多平台的,对于JVM以外的目标,我使用替代实现。

actor按照添加项目的相同顺序处理项目,并在单个协同程序中按顺序执行。这意味着内部转换将在外部转换完成后进行处理,当您在actor中使用actor时,您不能对其进行更改。我们不能启动更多的协程,因为我们将状态限制在单个线程中,否则可能会重复处理顺序。如果我们将转换标记为挂起函数,尝试将内部转换的作业加入到外部转换的主体中,只会导致死锁

你同意这种行为吗?如果不是,则不要使用参与者或嵌套变换。如果是,请提供一些用例,其中创建将在外部转换后处理的嵌套转换是有意义的

至于加入所有的工作,我有一些代码。大体上,我们有一个外部转换,它创建了一个内部转换。外部一个返回2,内部一个返回8,但是内部一个在外部一个完成后开始,所以结果是8。但正如您所希望的,transformJob.join main也会等待内部作业

私人封闭式再造林 私有类Getval延迟:CompletableDeferred:重新操作 私有类Transformval transformer:TransformStub.T->T,val stub:TransformStub,val作业:CompletableJob:重新操作 接口参考{ 乐趣:T 有趣的transformtransformer:TransformStub.T->T:Job } 接口转换存根{ 有趣的transformtransformer:TransformStub.T->T:Job } 私有类转换 val演员:SendChannel, val范围:CoroutineScope :转换存根{ 覆盖transformtransformer:TransformStub.T->T:Job{ 返回范围.launch{ val childJob:CompletableJob=Job val childStub=transformstubimpactor,此 actor.sendTransformer、childStub、childJob childJob.join } } } 类重新填充初始值:T:Ref{ private val actorJob=作业 私有val actorScope=CoroutineScopeactorJob private val actor=actorScope.actor{ 变量值:T=初始值 频道中的味精{ 当味精{ 是Get->{ printlget!$value msg.deferred.completevalue } 是转换->{ 味精{ val newValue=stub.transformervalue printlnTransform!$value->$newValue 值=新值 工作完成了 } } } } } 覆盖乐趣获取:T=runBlocking{ val deferred=CompletableDeferred actor.sendGetdeferred 等待 } 覆盖transformtransformer:TransformStub.T->T:Job{ val存根=TransformStubImpactor,GlobalScope 回线变压器 } } fun main=运行阻塞{ val ref:ref=RefImpl0 val transformJob=ref.transform{ 变换{8} 2. } 转换job.join ref.get } actor按照添加项目的相同顺序处理项目,并在单个协同程序中按顺序执行。这意味着内部转换将在外部转换完成后进行处理,当您在actor中使用actor时,您不能对其进行更改。我们不能启动更多的协程,因为我们将状态限制在单个线程中,否则可能会重复处理顺序。如果我们将转换标记为挂起函数,尝试将内部转换的作业加入到外部转换的主体中,只会导致死锁

你同意这种行为吗?如果不是,则不要使用参与者或嵌套变换。如果是,请提供一些用例,其中创建将在外部转换后处理的嵌套转换是有意义的

至于加入所有的工作,我有一些代码。大体上,我们有一个外部转换,它创建了一个内部转换。外一个返回2,内一个返回8,但内一个在外一个完成后开始 一,结果是8。但正如您所希望的,transformJob.join main也会等待内部作业

私人封闭式再造林 私有类Getval延迟:CompletableDeferred:重新操作 私有类Transformval transformer:TransformStub.T->T,val stub:TransformStub,val作业:CompletableJob:重新操作 接口参考{ 乐趣:T 有趣的transformtransformer:TransformStub.T->T:Job } 接口转换存根{ 有趣的transformtransformer:TransformStub.T->T:Job } 私有类转换 val演员:SendChannel, val范围:CoroutineScope :转换存根{ 覆盖transformtransformer:TransformStub.T->T:Job{ 返回范围.launch{ val childJob:CompletableJob=Job val childStub=transformstubimpactor,此 actor.sendTransformer、childStub、childJob childJob.join } } } 类重新填充初始值:T:Ref{ private val actorJob=作业 私有val actorScope=CoroutineScopeactorJob private val actor=actorScope.actor{ 变量值:T=初始值 频道中的味精{ 当味精{ 是Get->{ printlget!$value msg.deferred.completevalue } 是转换->{ 味精{ val newValue=stub.transformervalue printlnTransform!$value->$newValue 值=新值 工作完成了 } } } } } 覆盖乐趣获取:T=runBlocking{ val deferred=CompletableDeferred actor.sendGetdeferred 等待 } 覆盖transformtransformer:TransformStub.T->T:Job{ val存根=TransformStubImpactor,GlobalScope 回线变压器 } } fun main=运行阻塞{ val ref:ref=RefImpl0 val transformJob=ref.transform{ 变换{8} 2. } 转换job.join ref.get }
override fun transform(transformer: (T) -> T): Job {
    val job = Job()
    launch {
        actor.send(RefOperation.Transform(transformer, job))
    }
    return job
}
ref.transform {

  ...
  ref.transform {

  }
}