Разница между DispatchSourceTimer, Timer и asyncAfter?

Я изо всех сил пытаюсь понять ключевые различия между DispatchSourceTimer, Timer и asyncAfter (в моем случае для планирования задачи, которую нужно запускать каждые X секунд, хотя понимание различий в таймерах может быть полезно) (или есть другая (более эффективная) ) механизм планирования в Swift помимо перечисленных таймеров?).

Timer нуждается в активном цикле выполнения в текущей очереди, в которой он был запущен. DispatchSourceTimer не нуждается в этом. Timer удерживает процессор от перехода в состояние ожидания. Это относится и к DispatchSourceTimer/asyncAfter?

В какой ситуации Timer предпочтительнее, чем DispatchSourceTimer/asyncAfter? И, конечно, разница между всеми ними?

Я хочу планировать работу каждые 15 секунд в моем приложении в частной очереди. Это означает, что мне нужно использовать DispatchSourceTimer потому что я нахожусь в очереди, которая не является основным потоком (или добавляю цикл выполнения в очередь и использую Timer). Тем не менее, я не вижу никакой выгоды даже от использования Timer в первую очередь. Может быть, есть другая операция, которую я могу использовать, чтобы этот график работал каждые X секунд в частной очереди, которая более эффективна, чем DispatchSourceTimer, но я не нашел лучшего решения.

Является ли DispatchSourceTimer более эффективным, чем Timer? Или я должен пойти на метод с самостоятельным вызовом с asyncAfter?

Это код для создания таймеров.

asyncAfter

DispatchQueue.global().asyncAfter(deadline: .now() + .seconds(2)) {
    // Code
}

таймер

Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { (_) in
    // Code
}

DispatchSourceTimer

let timer = DispatchSource.makeTimerSource()

timer.schedule(deadline: .now() + .seconds(1))

timer.setEventHandler {
    // Code
}

timer.activate()

Каковы минусы и плюсы всех таймеров? Когда я должен использовать один над другим? Какой таймер наиболее эффективен? Я придумал следующее:

таймер

Плюсы:

  • Может быть признан недействительным
  • Ссылка не требуется
  • Может быть остановлено, пока это запланировано.

Минусы:

  • Предотвращает процессор для простоя
  • Необходимо запустить в очереди с циклом выполнения (иначе ничего не происходит, даже нет триггера подтверждения...)

DispatchSourceTimer

Плюсы:

  • Может быть отменено
  • Не требуется цикл выполнения

Минусы:

  • Нужна сильная ссылка, иначе он будет немедленно освобожден

asyncAfter

Плюсы: - Не требуется цикл запуска

Минусы: - отменить нельзя (я думаю)

Есть ли еще таймеры? Почему так много таймеров? Я ожидал реальной разницы между таймерами, но не смог их найти.

Здесь много вопросов, как вы можете прочитать. Главный вопрос: какие таймеры доступны и какие таймеры следует использовать в каком случае и почему?

Ответ 1

Таймер - это быстрый мост NSTimer, который восходит к NeXTSTEP, задолго до появления Grand Central Dispatch (GCD) и таких вещей, как DispatchSourceTimer, который появился только до 10.6 (в форме dispatch_source_set_timer) и dispatchAfter (в форме отправки_после).

NSTimer основан на цикле выполнения, который был основным способом параллелизма до GCD. Это совместная параллельная система, предназначенная для работы в одном потоке на одном ядре (хотя она может быть расширена до многопоточных сред).

Хотя цикл выполнения все еще очень важен в Cocoa, он больше не является основным или даже предпочтительным способом управления параллелизмом. Начиная с 10.6, GCD становится все более предпочтительным подходом (хотя добавление API NSTimer на основе блоков в период 10.12 было желанной модернизацией).

На 15-секундной шкале различия в эффективности не имеют значения. Тем не менее, я не понимаю ваш комментарий "Таймер удерживает процессор от перехода в состояние ожидания". Я не верю, что это правда. Процессор определенно все еще перейдет в состояние ожидания при ожидании срабатывания NSTimer.

Я бы не стал запускать цикл запуска только для запуска NSTimer. Вам было бы намного лучше запланировать это в главном цикле выполнения, а затем использовать DispatchQueue.async для выполнения реальной работы в какой-либо другой очереди.

Как правило, я использую инструмент самого высокого уровня, который отвечает потребностям. Это те, которые Apple, вероятно, оптимизирует лучше всего со временем, когда я внесу наименьшее количество изменений. Например, даты пожара NSTimer автоматически корректируются для повышения энергоэффективности. С DispatchSourceTimer вы получаете контроль над настройкой leeway чтобы получить то же преимущество, но вы можете установить его (значение по умолчанию равно нулю, что оказывает наименьшее влияние на энергию). Конечно, верно и обратное. DispatchSourceTimer является самым низким уровнем и дает вам максимальный контроль, поэтому, если это то, что вам нужно, то тот, который нужно использовать.

Для вашего примера я лично, вероятно, использовал бы Таймер и просто отправлял бы в частную очередь как часть блока. Но DispatchSourceTimer будет полностью уместным.

asyncAfter - это действительно другая вещь, так как это всегда один выстрел. Это замечательно, если вы хотите один выстрел, но он меняет вещи, если вы хотите повторить. Если вы просто вызываете asyncAfter в блоке для повторения, это будет через 15 секунд после того, как вы в последний раз закончили, вместо того, чтобы быть разнесенными на 15 секунд. Первый будет иметь тенденцию дрейфовать немного позже со временем. Вопрос в следующем: если по какой-то причине ваша задача заняла 5 секунд, хотите ли вы, чтобы следующее событие пожара произошло через 15 секунд после его окончания, или вы хотите, чтобы между каждым событием пожара оставались постоянные 15 секунд? Ваш выбор там будет определять, какой инструмент является правильным.

В качестве небольшого примечания, события NSTimer всегда немного позже, чем они запланированы. Мероприятия GCD с задержкой могут быть немного раньше или немного позже. С практической точки зрения, не существует такой вещи, как "вовремя" (период нулевой длины; вы не попадете в него). Таким образом, вопрос всегда в том, обещали ли вы опоздать, как NSTimer, или вы можете быть рано, как GCD с свободой действий.