У меня есть класс, который использует actor
для обеспечения безопасности потоков общего изменяемого состояния. Я сделал небольшую обертку вокруг этого actor
, чтобы упростить его использование:
interface Ref<T : Any> {
fun get(): T
fun transform(transformer: (T) -> T): Job
}
Здесь get
использует runBlocking
для блокировки, пока не получит фактическое значение T
:
override fun get(): T = runBlocking {
val deferred = CompletableDeferred<T>()
launch {
actor.send(RefOperation.Get(deferred))
}
deferred.await()
}
и transform
делают нечто подобное без runBlocking
и просто возвращают Job
:
override fun transform(transformer: (T) -> T): Job {
val job = Job()
launch {
actor.send(RefOperation.Transform(transformer, job))
}
return job
}
Это нормально, пока вызов transform
не приведет к другому:
ref.transform {
...
ref.transform {
}
}
Здесь у меня есть 2 Job
с, но нет способа объединить их в один Job
, на который я могу позвонить join()
, если я хочу дождаться их завершения.
Решением для этого будет структурированный параллелизм, но тогда я больше не знаю, как создать мой actor
, поскольку он определен как расширение для CoroutineScope
.
Как я могу продолжать использовать actor
при сохранении возможности использовать структурированный параллелизм?
Обратите внимание, что я создал Ref
, потому что мой проект является мультиплатформенным, и для целей, отличных от JVM, я использую альтернативные реализации.