Может ли сопрограмма, запущенная через runBlocking, выполняться в двух или более потоках?

У меня есть этот код runBlocking, и я хотел знать, гарантировано ли, что сопрограмма, запущенная через runBlocking, всегда будет выполняться в одном и том же потоке (т. е. потоке, в котором начал выполняться runBlocking)?

1-й println покажет, скажем, Thread t1. Но после выполнения задержки функции приостановки я хочу знать, возобновится ли эта сопрограмма в том же потоке t1 или ее можно будет выполнить в другом потоке потока t2? Это связано с тем, что сопрограмма может выполняться в двух или более потоках, поэтому сопрограмма, созданная с помощью runBlocking, может выполняться в потоке t1, приостанавливаться при задержке() и возобновляться в другом потоке t2.

fun main(): Unit = runBlocking(Dispatchers.Default) { // Thread t1
  println("runBlocking start: ${Thread.currentThread().name}") // Thread t1

  delay(3000)

  println("runBlocking end: ${Thread.currentThread().name}") // is it necessary to be t1 ?
}

Вывод, который я получаю каждый раз: (но я чувствую, что во второй строке вывода это также могло быть DefaultDispatcher-worker-2): --

запуск runBlocking: DefaultDispatcher-worker-1

Конец runBlocking: DefaultDispatcher-worker-1

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
51
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Да, если вы укажете многопоточный диспетчер, например Dispatchers.Default.

Попробуйте запустить новую сопрограмму или две внутри runBlocking:

fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")

fun main(): Unit = runBlocking(Dispatchers.Default) {
  log("runBlocking start")
  async { delay(50) }.await()
  log("runBlocking end")
}

В конце концов, будут напечатаны разные потоки:

[DefaultDispatcher-worker-1 @coroutine#1] runBlocking start
[DefaultDispatcher-worker-2 @coroutine#1] runBlocking end

Ответ @jan-itor правильный и решил мой вопрос.

Хотя использование DefaultDispatcher каждый раз печатает один и тот же поток для моего ПК.

Я создал свой собственный CorotuineDispatcher, чтобы продемонстрировать, что эта сопрограмма также может выполняться в двух разных потоках.

fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")

fun main(): Unit = runBlocking(Executors.newFixedThreadPool(100).asCoroutineDispatcher()) {
  log("runBlocking start") // [pool-1-thread-1] runBlocking start
  async { delay(500) }.await()
  log("runBlocking end") //[pool-1-thread-4] runBlocking end
}

На моей машине ответ @jan-itor работает иногда, но в основном обе строки регистрируются в одном потоке. Ваш код каждый раз показывает журналы в разных потоках. Кроме того, с вашим кодом вам не нужно звонить async: просто вызвать delay тоже подойдет. Однако обратите внимание, что ваша программа не завершается. В конце вам нужно закрыть исполнителя.

k314159 27.06.2024 16:24

@ k314159 k314159 Спасибо за ясность. Да, закрывать пользовательский диспетчер — это хорошая практика. Я забыл это сделать.

Chapo144 28.06.2024 06:39

В отличие от ответа @jan-itor, вам не нужно заключать вызов delay в async. Если вы вызовете runBlocking в двух отдельных потоках, вы увидите эффект только от вызова delay:

fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")

fun main() {
    repeat(2) { rep ->
        thread {
            runBlocking(Dispatchers.Default) {
                log("$rep: runBlocking start")
                delay(50)
                log("$rep: runBlocking end")
            }
        }
    }
}

Выход:

[DefaultDispatcher-worker-1] 1: runBlocking start
[DefaultDispatcher-worker-2] 0: runBlocking start
[DefaultDispatcher-worker-2] 1: runBlocking end
[DefaultDispatcher-worker-1] 0: runBlocking end

Это показывает, что при вызове runBlocking повтора 1 начался в потоке DefaultDispatcher-worker-1 и закончился в потоке DefaultDispatcher-worker-2, а в повторении 0 все было наоборот.

Вы можете увидеть это более четко, если запустите опцию -Dkotlinx.coroutines.debug VM:

[DefaultDispatcher-worker-2 @coroutine#2] 1: runBlocking start
[DefaultDispatcher-worker-1 @coroutine#1] 0: runBlocking start
[DefaultDispatcher-worker-6 @coroutine#1] 0: runBlocking end
[DefaultDispatcher-worker-1 @coroutine#2] 1: runBlocking end

Другие вопросы по теме

Похожие вопросы

Как сопоставить тип базы данных массива с Kotlin
Как обрабатывать запросы на увольнение из диалога, запущенного из Composable, переданного как переменная
Как исправить проблему с KMP. Инициализируйте хотя бы одну цель Kotlin в «composeApp (:composeApp)» или «shared»?
Плагин [id: 'kotlin-android', версия: '1.7.10'] не был найден ни в одном из следующих источников в Android Studio для Flutter
Текст с обводкой или границами в наборе реактивного ранца
Ошибка Kotlin: java.lang.RuntimeException: не удалось найти реализацию для com.example.tutorialfollowing.AppDatabase. AppDatabase_Impl не существует
Ошибка размера анимированной бутылки с водой при составлении реактивного ранца
Рукоять кинжала: «Невозможно создать экземпляр ViewModel» при использовании внедрения зависимостей
Невозможно получить доступ к классу Retrofit2.Response. Проверьте путь к классам вашего модуля на предмет отсутствующих или конфликтующих зависимостей
Как я могу проверить, реализует ли класс интерфейс в Котлине?