2009-11-15 2 views
8

Как подождать, пока отдельный отрезок будет завершен на C++?Подождите, пока выделенная нить закончится на C++

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

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

  1. Я называю ThirdParty, и зарегистрировать функцию обратного вызова
  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(); 

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

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

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

EDIT: Подробнее:

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

Я проверил тест, создав Wait on the heap и сознательно пропуская память (т. Е. Объекты Wait никогда не освобождаются), и это не привело к сбоям. Поэтому я уверен, что это проблема «Подождите, если вы освободите ее слишком рано».

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

EDIT: ThirdParty детали:

Я не думаю, что это было актуально в первый, но чем больше я думаю об этом, тем больше я думаю, что это реальная проблема:

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

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

ответ

3

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

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

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

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

+0

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

+0

Кроме того, если мы используем статический Wait, возникает проблема с попыткой координировать, какой поток необходимо возобновить. – Tim

+0

Хорошо, вы можете это сделать.Вы можете добавить поле refcount к объекту Wait, защищенному глобальным мьютексом. Запустите refcount на 2, а затем выполните обратный вызов, и официант уменьшит пересчет при завершении. Если глобальный мьютекс становится вашим узким местом, существуют и другие более сложные решения. –

0

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

0

Вы правильно инициализируете и уничтожаете мьютекс и условие 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

Да, мьютексы и cond инициализируются/уничтожаются должным образом. Я на самом деле использую классы-оболочки для тех, которые были хорошо протестированы. И да, я уверен, что Wait будет преждевременно уничтожен - пока один поток по-прежнему находится в Wait :: callback. – Tim

0

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

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

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

Смежные вопросы