Как «try_lock» рекурсивный мьютекс, чтобы избежать сбоев во время уничтожения

#c #qt

#c #qt

Вопрос:

У меня есть класс, который постоянно выполняет вызовы rest с использованием таймера, результат этих вызовов rest отправляется обратно в класс и обрабатывается впоследствии. Проблема в том, что если деструктор этого класса вызывается во время процесса обновления, это приводит к сбою. Я попытался решить эту проблему с помощью рекурсивного мьютекса;

 private:
    QTimer*                                      m_Timer = nullptr;
    std::recursive_mutex                         m_RecursiveMutex;
 

Размещение одного вызова блокировки для деструктора и try_lock запуск функции обновления;

 Class::~Destructor()
{
    std::lock_guard<std::recursive_mutex> lock(m_RecursiveMutex);
    m_Timer->stop();
    delete m_Timer;
}

void Class::UpdateSelf(GetInfoResponse res)
{
    if (m_RecursiveMutex.try_lock())
{
    ... do stuff
    }
}
 

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

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

1. Действительно ли ваш код использует потоки? Я подозреваю, что вам было бы гораздо лучше попытаться понять причину первоначального сбоя, а не пытаться исправить ситуацию с помощью мьютекса. В качестве отступления, просто сделайте m_Timer a QTimer , а не a QTimer * — на самом деле нет необходимости в дополнительном усложнении управления распределением / удалением кучи.

2. Тот факт, что ваш объект может быть вызван независимо от того, существует он или нет, говорит мне, что мьютекс здесь не проблема. Отладьте свой код и проанализируйте время жизни объекта. Почему он используется после того, как деструктор уже был вызван?

3. Похоже, что вы делаете неправильно, удаляя Class объект из потока A, в то время как поток B все еще потенциально может получить доступ к Class объекту. Простое добавление мьютекса не решит эту проблему; вам нужно будет придумать способ гарантировать, что поток B больше не сможет получить доступ к объекту, прежде чем поток A удалит объект. Подход с большим молотком состоял бы в том, чтобы поток A сообщал потоку B о выходе, затем поток A вызывает wait() / join() для блокировки до тех пор, пока поток B фактически не завершится, затем поток A удаляет объект, затем (необязательно) поток A впоследствии порождает новый поток B.