Дождитесь завершения отсоединенного потока в C++

как я могу дождаться завершения отсоединенного потока на C++?

меня не волнует статус выхода, я просто хочу знать, закончился ли поток.

Я пытаюсь предоставить синхронную оболочку вокруг асинхронного инструмента thirdarty. Проблема заключается в странном состоянии гонки аварии с участием обратного вызова. Прогрессия:

  1. Я звоню третьей стороне и регистрирую обратный вызов
  2. когда thirdparty заканчивает, оно уведомляет меня с помощью обратного вызова - в отдельном потоке у меня нет реального контроля.
  3. Я хочу, чтобы поток из (1) ждал, пока не будет вызван (2).

Я хочу, чтобы обернуть это в механизм, который обеспечивает блокировку вызова. До сих пор я:

class Wait {
  public:
  void callback() {
    pthread_mutex_lock(&m_mutex);
    m_done = true;
    pthread_cond_broadcast(&m_cond);
    pthread_mutex_unlock(&m_mutex);
  }

  void wait() {
    pthread_mutex_lock(&m_mutex);
    while (!m_done) {
      pthread_cond_wait(&m_cond, &m_mutex);
    }
    pthread_mutex_unlock(&m_mutex);
  }

  private:
  pthread_mutex_t m_mutex;
  pthread_cond_t  m_cond;
  bool            m_done;
};

// elsewhere...
Wait waiter;
thirdparty_utility(&waiter);
waiter.wait();

насколько я могу судить, это должно сработать, и обычно это так, но иногда он падает. Насколько я могу определить из corefile, моя догадка о проблеме такова:

  1. когда обратный вызов передает конец m_done, поток ожидания просыпается
  2. поток ожидания теперь сделан здесь, и ожидание уничтожено. Все члены Wait уничтожены, включая мьютекс и cond.
  3. поток обратного вызова пытается продолжить с точки трансляции, но теперь использует освобожденную память, что приводит к повреждению памяти.
  4. когда поток обратного вызова пытается вернуться (выше уровня моего плохого метода обратного вызова), программа сбои (обычно с SIGSEGV, но я видел SIGILL пару раз).

Я пробовал много разных механизмов, чтобы попытаться исправить это, но ни один из них не решит проблему. Я все еще вижу случайные аварии.

редактировать: Подробнее:

Это часть многопоточного приложения, поэтому создание статического ожидания нецелесообразно.

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

Я также пробовал тест с sleep(5) после разблокировки в wait, и это также не привело к сбоям. Я ненавижу полагаться на таких клуджей.

редактировать: детали ThirdParty:

сначала я не думал, что это важно, но чем больше я думаю об этом, тем больше я думаю, что это реально проблема:

материал thirdparty, который я упомянул, и почему у меня нет контроля над потоком: это использование CORBA.

таким образом, возможно, что Корба держит ссылку на мой объект дольше, чем предполагалось.

4 ответов


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

вы также будете постоянно использовать эту память, она не будет освобождения. Но не похоже, что это плохо.

основная проблема заключается в том, что трудно координировать время жизни вашего потока коммуникационные конструкции между потоками: вам всегда понадобится хотя бы одна оставшаяся коммуникационная конструкция для связи, когда ее безопасно уничтожить (по крайней мере, на языках без сборки мусора, таких как C++).

редактировать: См. комментарии для некоторых идей о refcounting с глобальным мьютексом.


насколько мне известно, нет портативного способа напрямую спросить поток, если он выполнен (т. е. нет ). Что вы делаете is правильный способ сделать это, по крайней мере, до состояния, которое вы сигнализируете. Если вы видите сбои, что из-за Wait объект освобождается, когда поток, который его создает, завершает работу (а не какой-то другое тонкая проблема блокировки -- все слишком часто), проблема в том, что вам нужно убедитесь, что Wait не освобождается, управляя из потока, отличного от того, который делает уведомление. Поместите его в глобальную память или динамически выделите его и поделитесь им с этим потоком. У большинства просто нет потока, ожидаемого на собственной памяти для ожидания, есть поток, выполняющий ожидание, владеющий им.


вы инициализируете и уничтожаете мьютекс и условие var должным образом?

Wait::Wait()
{
    pthread_mutex_init(&m_mutex, NULL);
    pthread_cond_init(&m_cond, NULL);
    m_done = false;
}

Wait::~Wait()
{
    assert(m_done);
    pthread_mutex_destroy(&m_mutex);
    pthread_cond_destroy(&m_cond);
}

убедитесь, что вы не преждевременно разрушает Wait object -- если он будет уничтожен в одном потоке, в то время как другой поток все еще нуждается в нем, вы получите условие гонки, которое, вероятно, приведет к segfault. Я бы рекомендовал сделать его глобальной статической переменной, которая создается при инициализации программы (до main()) и разрушается при выходе из программы.


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

Static Wait невозможно. Как насчет Wait пул (он даже может расти по требованию)? Вы используете приложение для запуска пула потоков? Хотя шанс все равно будет тот же Wait будет повторно использоваться, пока сторонний модуль все еще использует его. Но вы можете свести к минимуму такой шанс, правильно queing вакантные ожидания в ваш бассейн.

отказ от ответственности: я никоим образом не эксперт в треде безопасности, поэтому рассматривать этот пост как предложение неспециалиста.