Использование таймера Swift в инструменте / демоне командной строки MacOS, который использует Swift-NIO для работы в сети: RunLoop vs wait issue
Я конвертирую инструмент / демон командной строки Swift MacOS для использования Swift-NIO для работы в сети. Это мой первый проект Swift-NIO.
Инструмент запускает таймер каждые 0,1 секунды. Вот строка внизу main.swift, которая запускает демон / цикл выполнения до преобразования Swift-NIO:
RunLoop.current.run()
Вот таймер в моем классе Universe.swift init(). Всегда существует ровно один экземпляр этого класса:
timer = Timer(timeInterval: 1.0 / updatesPerSecond, target: self, selector: #selector(timerFired), userInfo: nil, repeats: true)
timer?.tolerance = 0.3 / updatesPerSecond
debugPrint("Timer initialized")
if let timer = timer {
RunLoop.current.add(timer, forMode: RunLoop.Mode.common)
}
В этой конфигурации таймер срабатывает 10 раз в секунду, как и ожидалось. Но если я получаю какой-либо сетевой ввод, моя библиотека Swift-NIO вылетает, потому что она не находится в ожидаемом цикле событий.
В Swift-NIO я должен добавить строку channel.closeFuture.wait() в конец моего main.swift:
// This will never unblock as we don't close the ServerChannel.
try channel.closeFuture.wait()
RunLoop.current.run()
Это решает проблему сбоя Swift-NIO, но тогда я никогда не добираюсь до RunLoop таймера, поэтому мой таймер не срабатывает.
Как я могу использовать Swift-NIO для получения (и отправки) сетевых данных при работающем таймере?
Если это поможет, полный открытый исходный код этого проекта находится по адресу https://github.com/darrellroot/netrek-server-swift.
1 ответ
Лукаса был прав. Я упустил (и не понял) какой-то важный контекст.
Мой таймер в конечном итоге пытался отправить данные с помощью SwiftNIO следующим образом:
if let context = context {
let buffer = context.channel.allocator.buffer(bytes: data)
_ = context.channel.writeAndFlush(buffer)
}
Исправление заключалось в "диспетчеризации" отправки трафика в связанный с контекстом EventLoop:
if let context = context {
context.eventLoop.execute {
let buffer = context.channel.allocator.buffer(bytes: data)
_ = context.channel.writeAndFlush(buffer)
}
}
Он очень похож на DispatchQueue.main.async { } для обновления графического интерфейса в приложениях iOS, но с другой терминологией, связанной с Swift-NIO.