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

StackOverflow https://stackoverflow.com/questions/1736403

Вопрос

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

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

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

  1. Я звоню третьей стороне и регистрирую обратный вызов
  2. когда третья сторона завершает работу, она уведомляет меня, используя обратный вызов - в отдельном потоке, над которым у меня нет реального контроля.
  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();

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

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

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

Редактировать:Более подробная информация:

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

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

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

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

Сначала я не думал, что это имеет значение, но чем больше я думаю об этом, тем больше мне кажется, что это настоящая проблема:

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

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

Это было полезно?

Решение

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

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

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

РЕДАКТИРОВАТЬ:Некоторые идеи по пересчету с помощью глобального мьютекса см. в комментариях.

Другие советы

Насколько мне известно, не существует переносимого способа напрямую спросить поток, завершен ли он (т.нет pthread_ функция).Что ты делаешь является правильный способ сделать это, по крайней мере, в том, что касается состояния, о котором вы сигнализируете.Если вы наблюдаете сбои, которые, как вы уверены, вызваны 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 объект - если он будет уничтожен в одном потоке, в то время как другой поток все еще нуждается в нем, вы получите состояние гонки, которое, скорее всего, приведет к сбою сегмента.Я бы рекомендовал сделать глобальную статическую переменную, которая создается при инициализации программы (до main()) и уничтожается при выходе из программы.

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top