2014-03-29 2 views
-1

Программа ниже представляет собой синхронизацию между двумя потоками с использованием Mutex.Синхронизация двух потоков - winapi

Он компилирует, работает и печатает то, что я хочу по порядку (чередующийся R/W для 2 потоков), но он падает после его завершения. Любая идея почему?

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

HANDLE hMutex, hWriteDone, hReadDone; 

int num, state; 

void Writer() 
{ 
    for(int x=10; x>=0; x--) 
    { 

     while (true) 
     { 

      if (WaitForSingleObject(hMutex, INFINITE) == WAIT_FAILED) 
      { 

       std::cout<<"In writing loop, no mutex!\n"; 

       ExitThread(0); 
      } 

      if (state == 0) 
      { 

       ReleaseMutex(hMutex); 

       WaitForSingleObject(hReadDone, INFINITE); 

       continue; 
      } 
      break; 
     } 
     std::cout<<"Write done\n"; 

     num= x; 

     state= 0; 

     ReleaseMutex(hMutex); 

     PulseEvent(hWriteDone); 
    } 
} 

void Reader() 
{ 
    while(true) 
    { 

     if (WaitForSingleObject(hMutex, INFINITE) == WAIT_FAILED) 
     { 
      std::cout<<"In reader, no mutex!\n"; 

      ExitThread(0); 

     } 
     if (state == 1) 
     { 
      ReleaseMutex(hMutex); 

      WaitForSingleObject(hWriteDone, INFINITE); 

      continue; 
     } 

     if (num == 0) 
     { 

      std::cout<<"End of data\n"; 

      ReleaseMutex(hMutex); 

      ExitThread(0); 
     } 
     else { 

      std::cout<<"Read done\n"; 

      state=1; 

      ReleaseMutex(hMutex); 

      PulseEvent(hReadDone); 

     } 
    } 
} 

void main() 
{ 

    HANDLE TName[2]; 
    DWORD ThreadID; 

    state= 1; 

    hMutex= CreateMutex(NULL, FALSE, NULL); 
    hWriteDone= CreateEvent(NULL, TRUE, FALSE, NULL); 
    hReadDone= CreateEvent(NULL, TRUE, FALSE, NULL); 

    TName[0]= CreateThread(NULL, 0, 
     (LPTHREAD_START_ROUTINE)Writer, 
     NULL, 0, &ThreadID); 

    TName[1]= CreateThread(NULL, 0, 
     (LPTHREAD_START_ROUTINE)Reader, 
     NULL, 0, &ThreadID); 

    WaitForMultipleObjects(2, TName, TRUE, INFINITE); 
    CloseHandle(TName); 

    getchar(); 
} 
+0

Когда вы вызываете 'WaitForSingleObject()', вы должны проверять 'WAIT_OBJECT_0' вместо' WAIT_FAILED'. Когда вы работаете с мьютексами, 'WaitForSingleObject()' может возвращать 'WAIT_ABANDONED', который вы не обрабатываете. –

ответ

1

lpStartAddress параметр CreateThread имеет тип LPTHREAD_START_ROUTINE. Который является функциональным указателем, совместимым с этой подписью:

DWORD WINAPI ThreadProc(LPVOID lpParameter); 

Таким образом, вам нужно предоставить то, что ожидает функция. Ваша функция Reader не соответствует счету. Изменить свою подпись, чтобы быть как это:

DWORD WINAPI Reader(LPVOID lpParameter) 
{ 
    .... 
} 

А также для Writer.

Каждый раз, когда вы делаете что-то, чтобы подавить предупреждение о компиляторе, вы торгуете легко диагностируемой ошибкой времени компиляции для сложной диагностики ошибки времени выполнения. Это очень плохая сделка. Поэтому, как правило, не используйте трансляции. Иногда вам нужно нарушить это правило, но делайте это в полном понимании того, что вы делаете.

Ваша функция main также имеет несколько поддельную подпись. Если вы не хотите, чтобы обрабатывать аргументы, то вы должны объявить его как это:

int main() 

Поскольку вы игнорируете идентификатор потока, вы можете также передать NULL для конечного параметра CreateThread.

Это тоже неправильно:

CloseHandle(TName); 

Параметр CloseHandle имеет тип HANDLE. Вы передаете указатель на массив. Вы должны сделать это:

CloseHandle(TName[0]); 
CloseHandle(TName[1]); 

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

+0

Пока это работало, моя программа по-прежнему падает. Несмотря на то, что это, возможно, решило проблему, оно не решило исходную проблему. –

+0

Прошу прощения за то, что мой ответ настолько разочаровывает вас. Вы исправили обе процедуры потока? –

+0

Это не так. Кажется, что если я делаю 'CloseHandle (TName [0]); \t CloseHandle (TName [1]); он работает. Почему так? –

2

Вы никогда не должны бросать указатель на функцию. Удалите (LPTHREAD_START_ROUTINE) от вашего кода, исправьте ошибки компилятора и повторите попытку. Никогда не используйте casts для подавления ошибок компилятора.

+0

Почему я не должен бросать fct. указатель? Я мог бы это сделать, но я хочу знать, почему. –

+0

Кастинг только говорит компилятору заткнуться, на самом деле ничего не исправить. Но что, если предупреждения компилятора действительно действительны? Если CreateThread хочет определенную подпись функции, вы должны ее предоставить. – tenfour

+0

... и если вы указали неправильную сигнатуру функции, это может привести к сбоям oddball, часто связанным со стеком. В этом случае ваш код, вероятно, сработает, потому что ваши функции не покидают стек в состоянии, которое ожидает CreateThread. – tenfour

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