2014-05-07 4 views
0

У меня вопрос о справедливости критических разделов в Windows, используя методы EnterCriticalSection и LeaveCriticalSection. Документация MSDN указывает: «Нет гарантии о том, что потоки получат право собственности на критический раздел, однако система будет честна для всех потоков». Проблема связана с приложением, которое я написал, которое блокирует некоторые потоки, которые никогда не входят в критический раздел, даже после долгого времени; поэтому я выполнил некоторые тесты с помощью простой программы c, чтобы проверить это поведение, но я заметил странные результаты, когда у вас много потоков, и некоторые моменты ожидания внутри. Это код тестовой программы:Критические разделы Windows критические

CRITICAL_SECTION CriticalSection; 

DWORD WINAPI ThreadFunc(void* data) { 
    int me; 
    int i,c = 0;; 
    me = *(int *) data; 
    printf(" %d started\n",me); 
    for (i=0; i < 10000; i++) { 
    EnterCriticalSection(&CriticalSection); 
    printf(" %d Trying to connect (%d)\n",me,c); 
    if(i!=3 && i!=4 && i!=5) 
     Sleep(500); 
    else 
     Sleep(10); 
    LeaveCriticalSection(&CriticalSection); 
    c++; 
    Sleep(500); 
    } 
    return 0; 
} 

int main() { 
    int i; 
    int a[20]; 
    HANDLE thread[20]; 

    InitializeCriticalSection(&CriticalSection); 
    for (i=0; i<20; i++) { 
     a[i] = i; 
     thread[i] = CreateThread(NULL, 0, ThreadFunc, (LPVOID) &a[i], 0, NULL); 
    } 
} 

Результаты этого является то, что некоторые потоки блокируются на протяжении многих циклов, а также некоторые другие ввести критическую секцию очень часто. Я также заметил, что если вы измените более быстрый сон (10 мс), все может вернуться к справедливости, но я не нашел никакой связи между временем сна и честностью. Однако этот тестовый пример работает намного лучше, чем мой реальный код приложения, что намного сложнее и показывает на самом деле голод для некоторых потоков. Чтобы убедиться, что голодные потоки живы и работают, я сделал тест (в своем приложении), в котором я убиваю потоки после ввода 5 раз в критическом разделе: результат состоит в том, что в конце каждый поток входит, поэтому я уверен, что все они живы и заблокированы на мьютексе. Должен ли я предполагать, что Windows действительно НЕ честна с потоками? Знаете ли вы какое-либо решение этой проблемы?

EDIT: тот же код в linux с pthreads работает, как и ожидалось (нет ничьих голодающих).

EDIT2: Я нашел рабочее решение, обеспечивающее справедливость, используя CONDITION_VARIABLE. Это можно сделать из этого сообщения (link) с необходимыми изменениями.

+2

В статье MSDN ничего не говорится о справедливости. Соответственно, с Vista и Server 2003 SP1 их нет. Справедливость вызывает блокировки конвоев, backgrounder [здесь] (http://joeduffyblog.com/2006/12/14/anticonvoy-locks-in-windows-server-2003-sp1-and-windows-vista/). –

+0

Семафоры, критические разделы и мьютексы - не всегда лучший выбор для синхронизации. Для сильно оспариваемых ресурсов вам нужно очень внимательно относиться к тому, что вы используете, иногда позволяя одному потоку управлять этим ресурсом, потому что вам не нужно блокировать какой-либо другой поток на этом ресурсе. Классическим примером может служить пользовательский интерфейс. – Mgetz

+0

@ HansPassant: Я прочитал статью, спасибо, поэтому я должен предположить, что не использовать структуру данных CriticalSection, потому что это несправедливо. Я процитировал эту ссылку: [link] (http://msdn.microsoft.com/en-us/library/windows/desktop/ms683472%28v=vs.85%29.aspx), и он действительно говорит, что я написал , – Mat

ответ

0

Вы все равно столкнетесь с голодающими проблемами, так как критическая часть хранится так долго.
Я думаю, что MSDN, вероятно, говорит о том, что планировщик справедлив в отношении пробуждения потоков, но поскольку нет порядка регистрации блокировки, тогда он может быть не «честным» так, как вы ожидаете. Вы пытались использовать мьютекс вместо критической секции? Кроме того, вы пытались настроить счетчик спинов?

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

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

В качестве альтернативы, возможно, взгляните на использование boost asio. Вы можете использовать threadpool и strands, чтобы предотвратить одновременное выполнение нескольких обработчиков async, в противном случае синхронизация была бы проблемой.

+0

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

+0

К сожалению, такое же поведение с мьютексом. – Mat

0

Я думаю, вы должны рассмотреть несколько вещей:

  • в 9997 10000 случаев переход к Sleep(500). Каждый поток содержит цитичную секцию в течение 500 мс почти на каждой успешной попытке получить критический раздел.

  • Нити выполняют еще один Sleep(500) после освобождения критической секции. В результате одна нить занимает почти 50% (49,985%) времени, проведя критический раздел - несмотря ни на что!

  • За кулисами: Joe Duffy: Списки ожидания для взаимоисключающих блокировок хранятся в порядке FIFO, и ОС всегда пробуждает поток в начале таких очередей ожидания.

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

Как долго вы выполняете тест/Какой процессор? И какая версия Windows? Вы должны уметь записывать еще несколько фактов: гистограмма потока с активным и нитьным идентификатором может многое рассказать о справедливости.

Критические участки должны быть приобретены в течение коротких промежутков времени. В большинстве случаев общие ресурсы можно решать гораздо быстрее. A Sleep внутри критического участка почти наверняка указывает на дефект дизайна.

Подсказка: Сократите время, проведенное внутри критической секции, или расследуйте Semaphore Objects.

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