Безопасно ли сигнализировать семафор перед деинициализацией на всякий случай?

      class SomeViewController: UIViewController {
    let semaphore = DispatchSemaphore(value: 1)

    deinit {
        semaphore.signal() // just in case?
    }

    func someLongAsyncTask() {
        semaphore.wait()
        ...
        semaphore.signal() // called much later
    }
}

Если я скажу семафору подождать, а затем деинициализировать контроллер представления, которому он принадлежит, до того, как семафору будет сказано подать сигнал, приложение вылетит с ошибкой. Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)ошибка. Однако, если я просто позвоню semaphore.signal()в методе контроллера представления катастрофа предотвращена. Однако, если асинхронная функция возвращается до deinitвызывается и контроллер представления деинициализируется, затем signal()вызывается дважды, что не кажется проблематичным. Но безопасно ли и/или разумно ли это делать?

1 ответ

Вы наткнулись на функцию/ошибку в . Если вы посмотрите на трассировку стека и перейдете к вершине стека, вы увидите сборку с сообщением:

ОШИБКА В КЛИЕНТЕ LIBDISPATCH: объект семафора освобождается во время использования

Например,

Это потому что DispatchSemaphoreпроверяет, связан ли семафор valueменьше при чем при init, и если это так, это терпит неудачу. Короче говоря, если значение меньше, libDispatch делает вывод, что семафор все еще используется.

Это может показаться чрезмерно консервативным, так как это обычно происходит, если клиент был небрежен, а не потому, что обязательно есть какая-то серьезная проблема. И было бы неплохо, если бы он выдавал значимое сообщение об исключении, а не заставляло бы нас копаться в трассировках стека. Но именно так работает libDispatch, и нам приходится с этим жить.

Из всего сказанного есть три возможных решения:

  1. У вас, очевидно, есть путь выполнения, где вы выполняете и не достигаете до того, как объект будет освобожден. Измените код, чтобы этого не произошло, и ваша проблема исчезла.

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

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

    См . https://lists.apple.com/archives/cocoa-dev/2014/Apr/msg00483.html .

  3. Итай перечислил ряд причин, по которым вообще не следует использовать семафоры. Есть и масса других причин:

    • Семафоры несовместимы с новой системой параллелизма Swift (см. Параллелизм Swift: за кулисами);
    • Семафоры также могут легко привести к взаимоблокировкам, если в коде нет точности;
    • Семафоры обычно несовместимы с отменяемыми асинхронными процедурами; и т.п.

    В настоящее время семафоры почти всегда являются неправильным решением. Если вы сообщите нам, какую проблему вы пытаетесь решить с помощью семафора, мы сможем порекомендовать другие, лучшие решения.


Вы сказали:

Однако, если асинхронная функция возвращается до deinitвызывается и контроллер представления деинициализируется, затем signal()вызывается дважды, что не кажется проблематичным. Но безопасно ли и/или разумно ли это делать?

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

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