【问题标题】:Valid way to reuse coroutine scope?重用协程范围的有效方法?
【发布时间】:2020-10-20 17:42:56
【问题描述】:

所以我有一个具有生命周期的类,其中包含一个 start() 和一个 stop() 函数来启动和停止生命周期。现在在start() 函数中我必须启动一个协程,在stop() 我必须取消它(start() 中的协程无限运行直到它被取消,有点像监听消息的套接字)。

因为我想要一个很好的语法来处理协程,所以我希望这个类实现CoroutineScope。问题是start()stop() 可以被多次调用。

我的问题是这个实现是否有效并且不包含任何错误。目的是制作一个可重复使用的CoroutineScope

class ReusableCoroutines() : CoroutineScope {

    private var job = Job()

    override val coroutineContext: CoroutineContext
        get() = job

    fun start() {
        job = Job()
        launch {
            println("Started and running!")
            while(true) {

            }
        }
    }

    fun stop() {
        job.cancel()
        println("Stoped")
    }

}

【问题讨论】:

  • 我可以看到这里发生的事情是你的工作开始了两次,第一个永远不会被取消,或者访问你的 coroutineContext 的东西以一个过时的 Job 结束。另外(只是为了确定)- 仅仅因为您将 cancel 发送到作业并不意味着它实际上已停止,您可以将您的 while(true) 循环更改为 while(isActive) 以确保它在取消时停止。
  • 你应该让job 可以为空并且如果它被启动两次就会失败(正如@Joozd 提到的),如果在范围停止时访问coroutineContext 也会失败(get() = checkNotNull(job) { "scope not started" })。您还需要考虑是否可以同时调用start 和/或stop,因为这可能导致竞争条件。此外,您在这里打破了结构化并发:kotlinlang.org/docs/reference/coroutines/basics.html -> 结构化并发
  • 放弃制作可重用CoroutineScope的希望。如果您有一个生命周期可以停止和启动的类,请使其包含一个具有单一生命周期的类,该生命周期只启动和停止一次。

标签: kotlin kotlin-coroutines coroutine


【解决方案1】:

免责声明:我不会这样做。除非您每秒创建数千个可重用对象,否则您不会从可重用对象中获得太多收益。我只创建在创建时初始化的一次性类(没有start 函数),然后在调用stop 时创建cancelled。话虽如此,这可能有效:

class ReusableCoroutines(
        private val scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : CoroutineScope by scope {

    private lateinit var job: Job
    private var stopped: Boolean = false
    private var started: Boolean = false

    @Synchronized
    fun start() {
        require(stopped.not()) {
            "Already stopped!"
        }
        require(started.not()) {
            "Already started!"
        }
        job = launch {
            println("Started and running!")
            while (true) {

            }
        }
    }

    @Synchronized
    fun stop() {
        require(stopped.not()) {
            "Already stopped!"
        }
        stopped = true
        cancel()
        println("Stopped")
    }

}

所以你需要一些同步 @Synchronized 来防止开始被多个线程调用并最终处于不一致的状态。

您还可以将scope 作为参数,这样您就可以获得结构化并发。您还可以在scope 中添加SupervisorJob,以防止在您取消此范围时取消父级。

【讨论】:

  • 谢谢,我会重新考虑我的设计选择,可能会采纳你的第一个建议。我做对了吗,我只有一个使用私有成员 Job 实现 CoroutineScope 的类。如果我需要停止它,我会调用一个函数 stop() 来取消作业,如果我再次需要它,我会创建一个此类的新对象?
  • 如果你只有一个协程,Job 就可以了。我唯一要更改的是将范围作为参数传递,以便您可以实现结构化并发。
猜你喜欢
  • 1970-01-01
  • 2019-05-02
  • 2021-04-02
  • 2017-03-24
  • 2021-12-24
  • 1970-01-01
  • 1970-01-01
  • 2019-06-22
  • 2019-04-27
相关资源
最近更新 更多