簡體   English   中英

如何在 Kotlin 的公共池下創建單線程協程上下文?

[英]How can I create single-thread coroutine context under Common pool in Kotlin?

簡短要求:能夠創建 corotine 上下文,該上下文僅在單線程中執行(例如,沒有並行性)。

附加要求:這些任務最好使用現有的 CommonPool(例如線程池)

實際上,kotlin 協程有newSingleThreadContext方法,它將創建單獨的線程並將所有任務調度到其中。 但是,這是專用線程,因此大約 1000 個這樣的上下文將需要大量資源。

因此,我想要具有以下特征的上下文:

  • 最多可同時執行一項任務
  • 這個上下文應該重用任何其他的(例如父上下文)。 例如,上下文不應包含額外的線程

這是一個解決方案:

例如,當您說withSerialContext(Dispatchers.Default) {doWork()} ,它在默認調度程序線程上執行doWork() ,但它的所有部分都將像在 runBlocking{} 中那樣一次執行一個。 請注意,即使一次只有一個線程,也不能保證整個操作都是同一個線程。

suspend fun <T> withSerialContext(
        context: CoroutineDispatcher,
        block: suspend CoroutineScope.() -> T
): T = withContext(SerialContextDispatcher(context), block)

private class SerialContextDispatcher(private val target: CoroutineDispatcher) : CoroutineDispatcher() {

    private val q = ConcurrentLinkedQueue<Runnable>()
    //Whoever CASes this false->true schedules execution of runproc
    private val pending = AtomicBoolean(false)
    //Only one of these runs at a time
    private val runproc = object: Runnable {
        override fun run() {
            while(true) {
                val proc = q.poll();
                if (proc != null) {
                    try {
                        proc.run()
                    }
                    catch (e: Throwable) {
                        target.dispatch(EmptyCoroutineContext, this)
                        throw e
                    }
                } else {
                    pending.set(false);
                    if (q.isEmpty() || !pending.compareAndSet(false, true)) {
                        return
                    }
                }
            }
        }
    }

    override fun dispatch(context: CoroutineContext, block: Runnable) {
        q.add(block)
        if (pending.compareAndSet(false, true)) {
            target.dispatch(EmptyCoroutineContext, runproc)
        }
    }
}

我發現,沒有簡單的解決方案來創建這樣的上下文。

githuib 上有未解決的問題 - https://github.com/Kotlin/kotlinx.coroutines/issues/261

我想我會更新這個問題然后我會找到正確的解決方案。

kotlinx.coroutines庫的1.6.0版本開始,我們可以在CoroutineDispatcher對象上使用limitedParallelism函數,它可以讓您限制並行度而無需創建額外的線程池,並提供了一種統一的方式來為未綁定並行度創建調度程序。

用法示例:

class UserRepository {
    private val dbDispatcher = Dispatchers.IO.limitedParallelism(1)

    suspend fun getUserById(userId: Int): User? = withContext(dbDispatcher) {
        executeQuery("SELECT * FROM users WHERE id = $1", userId).singleOrNull()
   }
}

應該可以解決以下問題:

最多可以同時執行一項任務,並且上下文不應包含額外的線程。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM