2013-04-01 2 views
0

У меня есть этот код:Почему эта синхронизация не работает?

#include <windows.h> 
#include <tchar.h> 
#include <stdio.h> 
#include <stdlib.h> 

#include <time.h> 

#define ITERATIONS 10 

typedef struct NUMERE 
{ 
    DWORD a; 
    DWORD b; 
} *PNUMERE; 

HANDLE ghThreadHandle[2]; 
HANDLE ghEvents[2]; 
//HANDLE hEvent; 

NUMERE nr; 

DWORD WINAPI GenerateNumbers(PNUMERE nr) 
{ 
    //PNUMERE nr = ((PNUMERE)param); 
    if(nr == NULL) 
     return -1; 

    nr->a = rand() % 100; 
    nr->b = (nr->a) * 2; 

    _tprintf(TEXT("Generated\n")); 

    //Sleep(10); 

    return 0; 
} 

DWORD WINAPI DisplayNumbers(PNUMERE nr) 
{ 
    //NUMERE nr = *((PNUMERE)param); 

    _tprintf(TEXT("Displayed: %d %d\n"),nr->a,nr->b); 

    return 0; 
} 

DWORD WINAPI DoStuff(PVOID param) 
{ 
    int index = *((int*)param); 

    for(unsigned int i = 0 ; i < ITERATIONS ; i++) 
    { 
     if(index == 0) 
     { 
      WaitForSingleObject(ghEvents[1],INFINITE); 
      ResetEvent(ghEvents[0]); 

      if(GenerateNumbers(&nr) == -1) 
       _tprintf(TEXT("GenerateNumbers error!\n")); 

      SetEvent(ghEvents[0]); 
      ResetEvent(ghEvents[1]); 
     } 
     else 
     { 
      WaitForSingleObject(ghEvents[0],INFINITE); 
      ResetEvent(ghEvents[1]); 

      DisplayNumbers(&nr); 

      SetEvent(ghEvents[1]); 
      ResetEvent(ghEvents[0]); 
     } 
    } 

    return 0; 
} 

DWORD GenerateThreads() 
{ 
    int temp0 = 0, temp1 = 1; 
    ghThreadHandle[0] = CreateThread(NULL 
     ,0 
     ,(LPTHREAD_START_ROUTINE)DoStuff 
     ,(LPVOID)&temp0 
     ,0 
     ,NULL); 

    if(NULL == ghThreadHandle[0]) 
     return -1; 

    ghThreadHandle[1] = CreateThread(NULL 
     ,0 
     ,(LPTHREAD_START_ROUTINE)DoStuff 
     ,(LPVOID)&temp1 
     ,0 
     ,NULL); 

    if(NULL == ghThreadHandle[1]) 
    { 
     CloseHandle(ghThreadHandle[0]); 
     return -1; 
    } 

    return 0; 
} 

int main() 
{ 
    srand(time(NULL)); 

    ghEvents[0] = CreateEvent(NULL,TRUE,TRUE,TEXT("MyEvent0")); 
    ghEvents[1] = CreateEvent(NULL,TRUE,TRUE,TEXT("MyEvent1")); 

    if(NULL == ghEvents[0] || NULL == ghEvents[1]) 
    { 
     _tprintf("Error creating events\n"); 
     return -1; 
    } 

    if(GenerateThreads() == -1) 
    { 
     _tprintf("Error GenerateThreads\n"); 
     return -1; 
    } 

    WaitForMultipleObjects(2,ghThreadHandle,TRUE,INFINITE); 

    //getchar(); 

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

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

    return 0; 
} 

Я хочу две функции (GenerateNumbers и DisplayNumbers), чтобы назвать в качестве альтернативы. Однако при запуске GenerateNumbers вызывается дважды, а затем он просто ждет. Метод DisplayNumbers никогда не вызывается. Может ли кто-нибудь объяснить причину этого тупика?

+1

Ваш пример не является самодостаточным. Неясно, какой «парам» вы передаете или сколько потоков вы создаете. Обратите также внимание на то, что вы создали оба события, которые были первоначально указаны, поэтому у вас есть состояние гонки прямо с места в карьер. –

+0

Создаю два потока. Оба они получают int как параметр (первый инициализируется 0, второй - 1). Это не проблема. Я проверил это. – conectionist

+0

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

ответ

3

Функция GenerateThreads передает адрес локальных переменных в другую цепочку (temp0 и temp1). Затем функция возвращает сразу после запуска потоков. Это означает, что другие потоки теперь получают доступ к памяти, которая была освобождена. Похоже, что к тому времени, когда потоки прочитали их param, память изменила значение на ноль, поэтому оба потока считают, что они являются потоком GenerateNumbers.

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

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

+0

Спасибо, миллион! – conectionist