如何同步协程?

21

我想确保这两种方法是同步的。我注意到协程比线程更难同步。我该如何保证如果我调用start()然后调用stop(),我的代码最终会被停止?

object Test {

    private val coroutine = CoroutineScope(Dispatchers.IO)

    @Synchronized
    fun start() {
        coroutine.launch {
            // some work
        }
    }

    @Synchronized
    fun stop() {
        coroutine.launch {
            // clean up then stop
        }
    }
}

我担心的是,我调用了start(),然后实际上先执行了stop()。因此,当我的代码应该停止时,它还在继续执行。


1
你想要类似于ExecutorService.submit(task)的语义吗?将一些工作单元排队并异步执行,但按提交顺序执行?如果是的话,那么你应该使用一个actor - Marko Topolnik
4个回答

20

同步或顺序执行命令是两种不同的事情。
如果您需要执行“start”再执行“stop”,只需不使用您自己的“coroutine.launch”创建新的协程,而是为函数使用suspend关键字,然后像这样执行它们:

suspend fun start() {}

suspend fun stop() {}

fun main() {
    scope.launch {
        start()
        stop()
    }
}

但是如果你想同步两个方法,你可以阅读Kotlin官方共享可变状态指南,其中提出了几种好的解决方案。然后做类似这样的事情:

val mutex = Mutex()

suspend fun main() {
    mutex.withLock {
        start()
        stop()
    }
}

3
你的第二个例子不起作用,因为Mutex.withLock是一个suspend函数,需要在suspend函数内部运行。 - José Roberto Araújo Júnior

1
如果您想在调用stop()时取消已启动的作业,请将作业保存在一个字段中:
object Test {

    private val coroutine = CoroutineScope(Dispatchers.IO)
    private var startedJob : Job? = null

    fun start() {
        startedJob = coroutine.launch {
            // some work
        }
    }

    fun stop() {
        runBlocking { startedJob?.cancelAndJoin() }
        coroutine.launch {
            // clean up then stop
        }
    }
}

--

如果你的问题是调用顺序——首先启动,然后停止——那么你需要检查和同步调用Test对象的代码。

0
使用带有布尔值的Deferred作业,然后在其上使用await()等待完成。类似这样:
fun doSomeWork() {
    GlobalScope.launch(Dispatchers.Main) {
        val isStartingComplete = start().await()
        // await() will wait for the completion of the start() function
        if (isStartingComplete) {
            stop()
        }
    }
}

fun start(): Deferred<Boolean> {
    return GlobalScope.async (Dispatchers.IO) {
        // Do your start work
        true
    }
}

fun stop() {
    // Do your stop work
}

仅供参考:Deferred不是函数,而是接口。https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/ - Jeel Vankhede
@JeelVankhede 是的,你是正确的,我会将“function”更新为“Job”,因为更符合要求。然而,对于给定的问题,代码应该可以正常工作。 - omz1990

-3
你只需要在 CoroutineScope 中有一个 Job:
object Test {

    private val scope = CoroutineScope(Dispatchers.IO + Job())

    fun start() {
        scope.launch {
            // some work
        }
    }

    fun stop() {
        scope.cancel()
    }
}


Job()是什么? - J_Strauton
@J_Strauton Job 定义了在协程中要执行的任何内容的生命周期。如果您启动新的协程 (或者更准确地说是挂起函数),它们的 "Jobs" 将成为您的 job 的子级,因此如果您取消协程,它们也将被取消 (清理自身)。 - Bohsen
请点击以下链接:https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/ 或者这个链接:https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-completable-job/index.html,因为我使用的 Job() 函数实际上返回一个 CompleteableJob - Bohsen
我认为你误解了问题,它是关于已启动任务的顺序。OP的第二个任务是“停止”,这就是他问“我的代码最终会被停止吗”的原因。他担心“停止”任务会被重新排序并在“开始”任务之前运行。 - Marko Topolnik
啊,好的。那我建议使用“Actor”代替。 - Bohsen
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接