Дождитесь завершения отдельного потока в C ++ - PullRequest
8 голосов
/ 15 ноября 2009

Как я могу дождаться окончания отдельного потока в 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();

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

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

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

РЕДАКТИРОВАТЬ : Подробнее:

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

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

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

РЕДАКТИРОВАТЬ : Сведения о третьей стороне:

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

Третья сторона, которую я упомянул, и почему я не могу контролировать поток: это использование CORBA.

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

Ответы [ 4 ]

3 голосов
/ 15 ноября 2009

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

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

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

EDIT : См. Комментарии для некоторых идей о пересчете с глобальным мьютексом.

0 голосов
/ 15 ноября 2009

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

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

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

0 голосов
/ 15 ноября 2009

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

0 голосов
/ 15 ноября 2009

Насколько мне известно, нет никакого портативного способа напрямую спросить у потока, завершен ли он (т.е. нет функции pthread_). То, что вы делаете , является правильным способом сделать это, по крайней мере, если у вас есть условие, которое вы сигнализируете. Если вы видите сбои, которые, как вы уверены, связаны с тем, что объект Wait освобождается при выходе из создавшего его потока (а не какой-то другой тонкой проблемы блокировки - все слишком часто) Вы должны убедиться, что Wait не освобожден, управляя из потока, отличного от того, который делает уведомление. Поместите его в глобальную память или выделите его динамически и поделитесь с этим потоком. В большинстве случаев поток не ожидает в собственной памяти для ожидания, а сам поток выполняет ожидание.

...