简体   繁体   English

如何在 Kotlin 的公共池下创建单线程协程上下文?

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

Short requirement: have ability to create corotine context, which will executed in the single thread only (eg without parallelism).简短要求:能够创建 corotine 上下文,该上下文仅在单线程中执行(例如,没有并行性)。

Additional requirement: it is better to use existing CommonPool (eg Thread Pool) for these tasks附加要求:这些任务最好使用现有的 CommonPool(例如线程池)

Actually kotlin coroutines have method newSingleThreadContext which will create separate thread and schedule all tasks into it.实际上,kotlin 协程有newSingleThreadContext方法,它将创建单独的线程并将所有任务调度到其中。 However, this is dedicated thread, so ~1000 such contexts will require a lot of resources.但是,这是专用线程,因此大约 1000 个这样的上下文将需要大量资源。

Therefore, I'd like to have context with the following characteristics:因此,我想要具有以下特征的上下文:

  • Maximum one task can be executed at the same time最多可同时执行一项任务
  • This context should reuse any other (eg parent context).这个上下文应该重用任何其他的(例如父上下文)。 Eg context should hold no additional threads例如,上下文不应包含额外的线程

Here's a solution:这是一个解决方案:

When you say, for example withSerialContext(Dispatchers.Default) {doWork()} , it executes doWork() on a default dispatcher thread, but all of its parts will execute one at a time like they do in runBlocking{}.例如,当您说withSerialContext(Dispatchers.Default) {doWork()} ,它在默认调度程序线程上执行doWork() ,但它的所有部分都将像在 runBlocking{} 中那样一次执行一个。 Note that even though it's one thread at a time, there is no guarantee that it will be the same thread for the whole operation.请注意,即使一次只有一个线程,也不能保证整个操作都是同一个线程。

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)
        }
    }
}

I found, that there are not simple solution to create such context.我发现,没有简单的解决方案来创建这样的上下文。

There is open issue on githuib - https://github.com/Kotlin/kotlinx.coroutines/issues/261 githuib 上有未解决的问题 - https://github.com/Kotlin/kotlinx.coroutines/issues/261

I think I will update this question then I will found right solution.我想我会更新这个问题然后我会找到正确的解决方案。

Starting from the 1.6.0 version of the kotlinx.coroutines library we can use limitedParallelism function on the CoroutineDispatcher object, which lets you limit parallelism without creating additional thread pools and provides a unified way to create dispatchers for unbound parallelism.kotlinx.coroutines库的1.6.0版本开始,我们可以在CoroutineDispatcher对象上使用limitedParallelism函数,它可以让您限制并行度而无需创建额外的线程池,并提供了一种统一的方式来为未绑定并行度创建调度程序。

Example of usage:用法示例:

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()
   }
}

It should solve the problems:应该可以解决以下问题:

Maximum one task can be executed at the same time and context should hold no additional threads.最多可以同时执行一项任务,并且上下文不应包含额外的线程。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Java中的单线程异步处理 - Single-Thread asynchronous processing in Java Kotlin 用于并行的协程以及我可以打开协程多长时间? - Kotlin Coroutines for Parallelism and How long i can have the coroutine open? 如何在Java中创建一个线程池,以便在每次运行时创建一个新线程? - How can I create a thread pool in Java that creates a new thread on each run? 如何在Java中创建一个不能同时运行某些任务的线程池? - How can I create a thread pool in Java that does not run certain tasks concurrently? Java并发性:我可以使用辅助线程创建池吗? - Java Concurrency: Can I create a pool using worker thread 如何使用Java中的LinkedBlockingQueue创建线程池? - How do I create a Thread Pool with a LinkedBlockingQueue in Java? 构造函数中的参数是什么(上下文,属性)? 我如何创建新的 object 这个 class? Java 和 Kotlin - What is parameters in constructor (context, attrs)? How I can create new object this class? Java and Kotlin 如何创建使用主线程池的单独线程池 - How to create separate thread pool that uses master thread pool 如何自定义spring boot embedded tomcat线程池? - How can I customize spring boot embedded tomcat thread pool? Java线程失效后将无法重新激活。 那么我们如何汇集一个线程来创建线程池呢? - Java Threads can not be re-activated once it is dead. Then how can we pool a Thread to create Thread Pool?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM