2009-12-28 2 views
5

Вчера вопрос о проверке с двойным проверкой запустил цепочку мыслей, которая оставила меня неуверенным в простой ситуации. В следующем коде можно ли нажать printf на «Больше не синхронизироваться»? В этом простом примере значения, вероятно, будут в одной и той же строке кэша, поэтому я думаю, что это будет менее вероятно (предполагая, что возможность начинается> 0%).Неужели WaitForSingleObject работает как барьер памяти?

Если ответ: «Нет, это невозможно». Тогда мой вопрос о последующей деятельности, скорее, предсказуем: почему бы и нет? До тех пор, пока вчера мои мысли не запутались и не обернулись вокруг многопоточного акселя, я предположил, что код будет в безопасности. Но теперь мне интересно, что предотвращает устаревшее чтение из кеша для одной из переменных pa или pb. И имеет ли значение, если pa, pb указывает на простые глобальные целочисленные переменные, а не на память malloc'd? Вызывает ли вызов WaitForSingleObject барьер памяти? Или если указатели будут объявлены нестабильными? Так много вопросов, так мало предложений.

Обновление: Я, наконец, нашел информацию, в которой конкретно говорится, что функции, которые используют объекты синхронизации сигналов, используют memory barriers. Это должно было быть очевидным, но мне не удалось найти окончательный ответ. Поэтому я снова могу обманывать себя, полагая, что я все понимаю.

int i1 = 0; 
int i2 = 0; 
int reads = 0; 
int done = 0; 
int *pa = NULL; 
int *pb = NULL; 
HANDLE hSync = NULL; 

DWORD WriteThread(LPVOID pvParam) 
{ 
    while(!done) 
     { 
     WaitForSingleObject(hSync, INFINITE); 
     (*pa)++; 
     (*pb)++; 
     ReleaseSemaphore(hSync, 1, NULL); 
     } 
    return 0; 
} 

DWORD ReadThread(LPVOID pvParam) 
{ 
    while(!done) 
     { 
     WaitForSingleObject(hSync, INFINITE); 
     if (*pa != *pb) 
     { 
     printf("No longer in sync: %d, %d\n", *pa, *pb); 
     exit(1); 
     } 
     ReleaseSemaphore(hSync, 1, NULL); 
     reads++; 
     } 
    return 0; 
} 

int main(int argc, char* argv[]) 
{ 
    DWORD dwID; 

    // malloc'd memory 
    pa = (int*)malloc(sizeof(int)); 
    pb = (int*)malloc(sizeof(int)); 

    // Is a simple global variable different? 
    //pa = &i1; 
    //pb = &i2; 

    *pa = 0; 
    *pb = 0; 

    hSync = CreateSemaphore(NULL, 1, 1, NULL); 
    CreateThread(NULL, 0, WriteThread, NULL, 0, &dwID); 
    CreateThread(NULL, 0, ReadThread, NULL, 0, &dwID); 

    while (*pa < 1000000) 
     Sleep(1); 
    done = 1; 

    return 0; 
} 

ответ

4

Это не имеет значения, где память лежит, и если бы это было все о кэш-когерентность, а затем объявить переменные летучий не будет делать ничего, чтобы исправить это. Семантика Volatile не является ни необходимой, ни достаточной для обеспечения безопасности потоков; не используйте его!

На уровне C/C++ pa и pb могут быть кэшированы в регистрах, но после любого вызова функции они будут считаться устаревшими. На уровне CPU все функции ожидания используют барьеры, чтобы убедиться, что все работает так, как ожидалось.

+0

+1 точно, что я собирался сказать. – tony

+0

Благодарим вас за информацию. Вы случайно знаете ссылку, в которой обсуждаются функции ожидания и барьеры памяти. Это то, что я искал и не видел. Вполне возможно, я просто слеп и пропустил что-то очевидное. –

+2

Ты не слепой; трудно найти соответствующую информацию в Интернете. MSDN предлагает достаточно хороший обзор на http://msdn.microsoft.com/en-us/library/ms686355%28VS.85%29.aspx. –

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