Как правильно вызывать деструкторы при выходе из потока?

#c #multithreading

Вопрос:

Контекст:

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

Моя проблема в том, что для выхода из программы используются сигналы прерывания, обработчик этих сигналов определен на самом низком уровне промежуточного программного обеспечения. Когда посылается сигнал прерывания, обработчик в конечном итоге вызывает exit(0) , что приводит к тому, что деструктор интерфейсного уровня вызывается тем же вызовом потока exit(0) . Внутри этого деструктора middlewareAppThread.join() вызывается, что приводит к взаимоблокировке, поскольку поток пытается присоединиться к самому себе.

Это упрощенное представление, извините, если оно грязное:

введите описание изображения здесь

Что меня смущает, так это то, что я вижу, как на прокси-объект ссылаются в глобальном пространстве имен, я думал, что деструктор будет вызываться основным потоком, но при отладке очевидно, что деструктор вызывается middlewareAppThread .

Решение, о котором я думал до сих пор, состоит в том, чтобы удалить обработчик сигналов из стека промежуточного программного обеспечения и создать его на уровне приложения/интерфейса. Это немного проблематично, так как промежуточное программное обеспечение является сторонним. Есть ли способ сохранить exit(0) вызов на самом низком уровне, когда деструкторы вызываются из основного потока?

Или: Что может быть причиной вызова деструктора из middlewareAppThread ?

Комментарии:

1. Вы можете позвонить exit , только если текущий поток является единственным запущенным. Необходимо просигнализировать потокам о завершении и join им перед вызовом exit . Не следует соединять потоки из деструкторов.

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

3. Это типично для того,что происходит, когда нетривиальное приложение пытается «изящно завершить». Есть ли какая-то важная причина, по которой вы не можете просто завершить процесс?

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

Ответ №1:

Это поток, который вызывает exit , который выполняет всю очистку.

Выполняется несколько шагов очистки:

  1. Деструкторы объектов с длительностью локального хранения потока, связанные с текущим потоком, деструкторы объектов со статической длительностью хранения и функции, зарегистрированные в std::atexit, выполняются одновременно

(курсив добавлен)

Я думал, что деструктор будет вызван основным потоком

Что заставляет тебя так думать? C не связывает объекты с потоками. Даже с локальным объектом потока можно взаимодействовать в другом потоке с помощью указателя или ссылки.

Есть ли способ сохранить exit(0) вызов на самом низком уровне

Да, у вас нет статических объектов длительности std::thread (под), особенно тех, которые могут вызывать exit .

с деструкторами, вызываемыми из основного потока?

Скоординируйте так, чтобы основной поток был тем, который нужно вызвать exit . Или просто вернуться из main

Комментарии:

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