2013-04-16 3 views
4

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

При поиске решения я заметил, что есть статьи, которые описывают, как реализовать функции WaitForMultipleObjects() в Linux с примерами, но эти примеры не удовлетворяют сценарию, который я должен поддерживать.

Сценарий в моем случае довольно прост. У меня есть процесс демона, в котором основной поток предоставляет метод/обратный вызов внешнему миру, например, в DLL. Код DLL не под моим контролем. Тот же основной поток создает новый поток «Thread 1». В потоке 1 должен выполняться вид бесконечного цикла, в котором он будет ждать события выключения (отключение демона), или он будет ждать события, доступного данным, через открытый метод/обратный вызов, упомянутый выше.

Короче говоря, поток будет ждать события выключения и события, имеющего доступ к данным, если если событие выключения сигнализируется о том, что ожидание будет удовлетворять, и цикл будет сломан или если будет передано событие с данными, то также ожидание будет удовлетворять, и поток будет выполняться обработка бизнеса.

В окнах кажется очень прямолинейным. Ниже приведен псевдословный код MS Windows для моего сценария.

//**Main thread** 

//Load the DLL 
LoadLibrary("some DLL") 

//Create a new thread 
hThread1 = __beginthreadex(..., &ThreadProc, ...) 

//callback in main thread (mentioned in above description) which would be called by the  DLL 
void Callbackfunc(data) 
{ 
    qdata.push(data); 
    SetEvent(s_hDataAvailableEvent); 
} 

void OnShutdown() 
{ 
    SetEvent(g_hShutdownEvent); 
    WaitforSingleObject(hThread1,..., INFINITE); 
    //Cleanup here 
} 

//**Thread 1** 

unsigned int WINAPI ThreadProc(void *pObject) 
{ 
    while (true) 
    { 

     HANDLE hEvents[2]; 
     hEvents[0] = g_hShutdownEvent; 
     hEvents[1] = s_hDataAvailableEvent; 

     //3rd parameter is set to FALSE that means the wait should satisfy if state of any one of the objects is signaled. 
     dwEvent = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE); 
     switch (dwEvent) 
     { 
      case WAIT_OBJECT_0 + 0: 
      // Shutdown event is set, break the loop 
      return 0; 

      case WAIT_OBJECT_0 + 1: 
      //do business processing here 
      break; 

      default: 
      // error handling 
     } 
    } 
} 

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

+0

POSIX эквивалент WFMO - это 'select' /' poll'/'epoll'. Почти все в Linux - это нужный файловый дескриптор, и вы всегда можете заставить некоторые трубы действовать как ожидаемые события interthread. Итак, ваш обработчик сигнала просто напишет трубку, связанную с другой нитью, которая будет разбужена. Вы даже можете использовать сокет datagram или датаграмму FIFO как свою потокобезопасную очередь. –

ответ

4

Правильный способ сделать это в Linux будет использовать переменные условия. Хотя это не то же самое, что WaitForMultipleObjects в Windows, вы получите ту же функциональность.

Используйте два bool s для определения наличия данных или выключения. Затем функция выключения и функция данных устанавливают соответственно bools и сигнализируют переменную условия.

#include <pthread.h> 

pthread_cond_t cv = PTHREAD_COND_INITIALIZER; 
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 

pthread_t hThread1; // this isn't a good name for it in linux, you'd be 
        // better with something line "tid1" but for 
        // comparison's sake, I've kept this 

bool shutdown_signalled; 
bool data_available; 

void OnShutdown() 
{ 
    //...shutdown behavior... 
    pthread_mutex_lock(&mutex); 
    shutdown_signalled = true; 
    pthread_mutex_unlock(&mutex); 
    pthread_cond_signal(&cv); 
} 

void Callbackfunc(...) 
{ 
    // ... whatever needs to be done ... 
    pthread_mutex_lock(&mutex); 
    data_available = true; 
    pthread_mutex_unlock(&mutex); 
    pthread_cond_signal(&cv); 
} 


void *ThreadProc(void *args) 
{ 
    while(true){ 
     pthread_mutex_lock(&mutex); 
     while (!(shutdown_signalled || data_available)){ 
      // wait as long as there is no data available and a shutdown 
      // has not beeen signalled 
      pthread_cond_wait(&cv, &mutex); 
     } 
     if (data_available){ 
      //process data 
      data_available = false; 
     } 
     if (shutdown_signalled){ 
      //do the shutdown 
      pthread_mutex_unlock(&mutex); 
      return NULL; 
     } 
     pthread_mutex_unlock(&mutex); //you might be able to put the unlock 
     // before the ifs, idk the particulars of your code 
    } 
} 
int main(void) 
{ 
    shutdown_signalled = false; 
    data_available = false; 
    pthread_create(&hThread1, &ThreadProc, ...); 
    pthread_join(hThread1, NULL); 
    //... 
} 

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

1

Если вы хотите написать UNIX или Линукс определенного код, у вас есть differenr API, доступный:

  • нити: обеспечивает нить, семафор, переменные условиями
  • IPC (интер процесс Comunication) механизмы: мьютекс, семафор , разделяемая память
  • сигналы

Для потоков, первая библиотека является обязательным (есть более низкие системные вызовы уровня на Linux, но это более утомительно). Для событий могут использоваться три.

Событие выключения системы вызывает завершение (SIG_TERM) и уничтожает (SIG_KILL) сигналы, передаваемые всем соответствующим процессам. Следовательно, также может быть инициировано закрытие отдельного демона.Цель игры - поймать сигналы и инициировать остановку процесса. Важными моментами являются:

  • механизм сигнала выполнен таким образом, что не надо ждать их

    Просто установить так называемый обработчик с помощью sigaction, и система сделает все остальное ,

  • сигнал устанавливается в процессе, и любой нить может перехватить его (обработчик может выполняться в любом контексте)

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

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

  • говорит удаленный хост, что сервер закрытия вниз,
  • закрыть сокет на чтение
  • процесса все остальные принятые команды/данные и присылать ответы
  • закрыть гнездо
  • выход

Для основного потока, это будет означать, инициируя объединение на рабочем потоке, а затем выйти.

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

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