Функции приостановки выполняются в другом потоке и возвращаются с результатом? - PullRequest
1 голос
/ 23 апреля 2020

Я просмотрел множество статей в Интернете, но я все еще немного озадачен тем, что конкретно происходит, шаг за шагом, когда функции приостановки приостанавливаются в сопрограммах.

Я знаю, что функция приостановки под капотом - просто обычная функция с параметром Continuation, позволяющим возобновить ее, но я не понимаю, куда идет эта функция приостановки или сопрограмма и где она возвращается после возобновления.

Я слышал Несколько человек говорят: «Они не обязательно возвращаются к одной и той же теме», и я не понимаю, может кто-нибудь объяснить мне это шаг за шагом?

Ответы [ 3 ]

1 голос
/ 23 апреля 2020

TLDR;

Нет гарантии, может или нет, это действительно зависит от следующих моментов:

  1. Является ли диспетчер многопоточным?
  2. Есть ли какое-либо переопределение диспетчера между ними?

ДЛИННЫЙ ответ

У сопрограммы есть CoroutineContext, который указывает, как он ведет себя, где он работает.

CoroutineContext в основном состоит из четырех элементов: Job, CoroutineName, CoroutineExceptionHandler и Dispatcher.

Ответственность диспетчера за отправку сопрограммы. Диспетчер может быть приостановлен, чтобы остановить даже выполнение сопрограмм (это полезно при модульном тестировании) , упомянутый здесь в android конференции , это может быть однопоточный диспетчер, такой же как Dispatchers.Main, это имеет событие-l oop, как javascript has.

Итак, это действительно зависит от следующих моментов:

  1. Является ли диспетчер многопоточным?

Например: это будет работать в одном потоке.

suspend fun main() {
    val dispatcherScope = CoroutineScope(Executors.newSingleThreadExecutor().asCoroutineDispatcher())

    val job = dispatcherScope.launch {
        repeat(10) {
            launch {
                println("I'm working in thread ${Thread.currentThread().name}")
                // every coroutine on same thread
            }
        }
    }
    job.join()
}

Запустите его здесь , другие однопоточные диспетчеры: Dispatchers.Main

Есть ли какое-либо переопределение диспетчера между ними?

В том же контексте, если мы переопределим диспетчер перед запуском, он изменит поток, даже если исходный контекст основан на однопоточном событии. l oop, каждая сопрограмма будет выполняться в отдельном потоке, создавая 10 различных потоков:

dispatcherScope.launch {
    repeat(10) {
        launch(Dispatchers.IO) {
            println("I'm working in thread ${Thread.currentThread().name}")
            // every coroutine on same thread
        }
    }
}

Запустите его здесь , другие многопоточные диспетчеры: Dispatchers.Default, диспетчер на основе исполнителя, Dispatchers.Unconfined (это сопрограмма запуска в любой бесплатной теме).

1 голос
/ 23 апреля 2020

Краткий ответ: они выполняют где-то и возвращаются с результатом к где-то .

Длинное (er) объяснение короткого ответа:

«Где-то» может быть тем же потоком, это может быть другой поток - это зависит от диспетчера и во многих случаях от текущего состояния диспетчера. Например, содержимое SequenceScope будет (по умолчанию) работать в том же потоке.

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

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

Поскольку люди, говорящие «они не обязательно возвращаются в один и тот же поток», это правильно по тем же причинам. Имейте в виду, что у вашего диспетчера может быть много потоков, и тот, который вы использовали до того, как вас приостановили, может быть занят другой функцией прямо сейчас, поэтому диспетчер просто выберет другой поток для запуска вашего кода.

0 голосов
/ 24 апреля 2020

Когда сопрограмма приостанавливается, базовый метод Java возвращает специальное значение COROUTINE_SUSPENDED. Если вызывающая функция также является приостановленной, она также возвращает объект, и поэтому выполнение возвращается к самой внутренней простой, не приостановленной функции. Эта функция обычно запускает событие l oop, где обработчики событий являются вызовами continuation.resume(). Итак, теперь он готов взять следующий обработчик из очереди и возобновить другую сопрограмму.

Когда вы вызываете continuation.resume(), само продолжение знает о диспетчере, отвечающем за сопрограмму, и делегирует его. Если текущий поток не принадлежит этому диспетчеру, он отправляет событие другому событию l oop, которое обслуживается пулом потоков диспетчера. Таким образом, диспетчер контролирует нить, в которой возобновляется сопрограмма.

...