[英]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:因此,我想要具有以下特征的上下文:
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.