Как отправить сопрограммы непосредственно в главный поток на JVM? - PullRequest
0 голосов
/ 15 января 2019

Я устанавливаю сетевую среду на основе kotlin сопрограммы для jvm. Классы Client и Server реализуют CoroutineScope, а переопределение для coroutinecontext - это Dispatchers.IO, так как я уверен, что это правильный Dispatcher для использования в таком случае. Однако я хочу обработать прочитанные пакеты в основном потоке или, по крайней мере, предоставить эту опцию. Не читая документацию, я использовал Dispatchers.Main, который теперь я понял, для потока пользовательского интерфейса Android. Есть ли диспетчер, который я могу использовать для запуска сопрограммы в главном потоке? Если нет, как бы я сделал один?

Я просмотрел документацию kotlin о том, как создать диспетчер, основанный на одном потоке, но я не смог найти ничего, кроме newSingleThreadContext, который создает новый поток. Я также понял, что возможно создать диспетчер из Java-исполнителя, но я все еще не уверен, как ограничить это уже существующим потоком.

class AbstractNetworkComponent : CoroutineScope {
    private val packetProcessor = PacketProcessor()
    private val job = Job()
    override val coroutineContext = job + Dispatchers.IO
}

class PacketProcessor : CoroutineScope {

    private val job = Job()
    override val coroutineContext = job + Dispatchers.Main //Android only!
    private val packetHandlers = mutableMapOf<Opcode, PacketHandlerFunc>()

    fun handlePacket(opcode: Opcode, packet: ReceivablePacket, networker: Writable) {
        launch(coroutineContext) {
            packetHandlers[opcode]?.invoke(packet, networker)
        }
    }
}

Так что с Dispatchers.Main я получаю исключение IllegalStateException из-за отсутствия компонента Android. Есть ли способ создать диспетчер, который блокирует основной поток до его завершения (как это делает runBlocking?) Спасибо!

Ответы [ 2 ]

0 голосов
/ 15 января 2019

runBlocking это именно то, что вам нужно. Он создает диспетчер и устанавливает его в контексте сопрограммы. Вы можете получить доступ к диспетчеру с помощью

coroutineContext[ContinuationInterceptor] as CoroutineDispatcher

и затем вы можете передать его объекту, который реализует CoroutineScope или что-либо еще, что вы хотите с ним сделать. Вот пример кода:

import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers.IO
import kotlin.coroutines.ContinuationInterceptor

fun main() {
    println("Top-level: current thread is ${Thread.currentThread().name}")
    runBlocking {
        val dispatcher = coroutineContext[ContinuationInterceptor]
                as CoroutineDispatcher
        ScopedObject(dispatcher).launchMe().join()
    }
}

class ScopedObject(dispatcher: CoroutineDispatcher) : CoroutineScope {
    override val coroutineContext = Job() + dispatcher

    fun launchMe() = launch {
        val result = withContext(IO) {
            "amazing"
        }
        println("Launched coroutine: " +
                "current thread is ${Thread.currentThread().name}, " +
                "result is $result")
    }
}

Это напечатает

Top-level: current thread is main
Launched coroutine: current thread is main, result is amazing
0 голосов
/ 15 января 2019

Согласно Руководство по программированию пользовательского интерфейса с сопрограммами kotlinx.coroutines имеет три модуля, которые предоставляют контекст сопрограммы для различных библиотек приложений пользовательского интерфейса:

Кроме того, диспетчер пользовательского интерфейса доступен через Dispatchers.Main из kotlinx-coroutines-core и соответствующая реализация (Android, JavaFx или Swing) обнаружена API-интерфейсом ServiceLoader. Например, если вы пишете приложение JavaFx, вы можете использовать расширение Dispatchers.Main или Dispachers.JavaFx, это будет тот же объект.

...