Быстрый заказ Async для печати?

Всегда ли это печатает в порядке 1 5 2 4 3?

print("1")
DispatchQueue.main.async {
    print("2")
    DispatchQueue.main.async {
        print(3)
    }
    print("4")
}
print("5")

Я чувствую, что ответ отрицательный, но я не могу это объяснить и надеюсь, что кто-то может прояснить мое понимание. Спасибо!

Ответ 1

Вы всегда будете получать 1, 2, 4, 3. 5 всегда будут после 1. Но там, где это заканчивается по отношению к другим, зависит от того, в какой очереди все началось.

Если это начато из основной очереди, то 5 всегда будет между 1 и 2.

Вот почему:

Этот код запускается в основной очереди. 1. Затем вы устанавливаете другой блок для асинхронного запуска в главной очереди, чтобы блок выполнялся после завершения текущего блока, а главная очередь доходила до конца текущего цикла цикла. Код продолжает следующую строку, которая должна печатать 5. Текущий блок завершается, и выполняется следующий блок в главной очереди. Это блок первого вызова DispatchQueue.main.async. Поскольку этот блок работает, он печатает 2 (так что теперь у нас есть 1 5 2). Другой блок помещается в очередь в основную очередь, как и в предыдущей. Текущий блок продолжается и печатает 4 (так что теперь у нас есть 1 5 2 4). Блок завершается, и выполняется следующий блок в главной очереди. Это заключительный блок, который мы добавили. Этот блок работает, и он печатает 3, давая окончательный результат 1 5 2 4 3.

Если вы начали с некоторой фоновой очереди, то 5 может появиться где-то после 1, но для такого простого кода 5, скорее всего, все еще будут отображаться между 1 и 2, но он может появиться где угодно после 1 в общем случае.

Вот почему:

Этот код начинается в фоновом режиме. 1. Затем вы устанавливаете другой блок для асинхронного запуска в основной очереди, чтобы блок выполнялся после завершения цикла запуска текущей основной очереди. Код продолжает следующую строку, которая должна печатать 5. Текущий блок заканчивается. Блок, добавленный в основную очередь, запускается параллельно фоновой очереди. В зависимости от времени запуска блока, добавленного в основную очередь, по отношению к оставшемуся коду в фоновом режиме, вывод print("5") может смешиваться с отпечатками из основной очереди. Вот почему 5 могут появиться где-то после 1, когда они начинаются с фона.

Но простой код в вопросе, скорее всего, всегда даст 1 5 2 4 3 даже при запуске на фоне, потому что код настолько короткий и занимает так мало времени.

Здесь некоторый код, который помещает 5 в другом месте:

func asyncTest() {
    print("1")
    DispatchQueue.main.async {
        print("2")
        DispatchQueue.main.async {
            print(3)
        }
        print("4")
    }

    for _ in 0...1000 {
    }

    print("5")
}

DispatchQueue.global(qos: .background).async {
    asyncTest()
}

Существование цикла приводит к тому, что 5 займет немного больше времени, прежде чем появится, что позволяет основной очереди выполнить кадровый процесс до того, как будет напечатано 5. Без цикла фоновый поток выполняется слишком быстро, так что 5 появляется перед 2.

Если вы используете этот тест на игровой площадке, добавьте:

PlaygroundPage.current.needsIndefiniteExecution = true

на вершину игровой площадки (сразу после импорта). Вам также понадобятся:

import PlaygroundSupport

Ответ 2

Это зависит от того, какой поток вы запускаете.

Если вы начнете с основного, тогда вы получите 1, 5, 2, 4, 3

Если вы начинаете с фонового потока, то большую часть времени вы получите их одинаковый результат (1, 5, 2, 4, 3), однако это не гарантируется, так как фоновый поток может быть послан в любой момент ОС, и если это произойдет прямо перед вызовом print(5), то 5 будет последним для печати.

Просто обратите внимание, что если код из вопроса является единственным в вашем приложении/игровой площадке, то вы можете быть удивлены тем, что работаете с частичными отпечатками, так как приложение/игровая площадка выходит, как только она попадает в строку print(5), прежде чем имея возможность выполнить асинхронную отправку. Чтобы обойти это, вы можете убедиться, что RunLoop.current.run() выполняется в основном потоке в качестве последней части вашего кода.

Вот несколько диаграмм, которые пытаются проиллюстрировать, что происходит в сценарии только для основного потока, и тот, где задействован фоновый поток:

enter image description here enter image description here