3

Я немного запутался относительно правильного использования критических разделов в многопоточных приложениях. В моем приложении есть несколько объектов (некоторые циклические буферы и объект последовательного порта), которые разделяются между потоками. Должен ли доступ к этим объектам всегда находиться в критических разделах или только в определенное время? Я подозреваю только в определенные моменты, потому что, когда я пытался обернуть каждое использование с помощью EnterCriticalSection/LeaveCriticalSection, я столкнулся с тем, что, казалось, было условием взаимоблокировки. Любое понимание, которое вы, возможно, цените. Благодарю.Многопоточные и критические разделы Использование - C++

ответ

6

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

Трудно дать больше советов, не зная больше о вашем коде, но вот некоторые общие моменты, о которых нужно помнить.

1) Критические разделы защиты ресурсы, а не процессы.

2) Ввести/оставить критические разделы в том же порядке по всем потокам. Если поток A входит в Foo, то входит в Bar, тогда поток B должен ввести Foo и Bar в том же порядке. Если вы этого не сделаете, вы можете создать гонку.

3) Ввод и вывоз должны выполняться в обратном порядке. Например, поскольку вы вошли в Foo, а затем ввели Bar, вы должны покинуть Bar, прежде чем покинуть Foo. Если вы этого не сделаете, вы можете создать тупик.

4) Держите замки на максимально короткий промежуток времени. Если вы закончили с Foo, прежде чем начать использовать Bar, отпустите Foo перед тем, как схватить Bar. Но вы все равно должны соблюдать правила порядка выше. В каждом потоке, который использует как Foo и Bar, вы должны приобрести и выпустить в том же порядке:

Enter Foo 
    Use Foo 
    Leave Foo 
    Enter Bar 
    Use Bar 
    Leave Bar 

5) Если вы только читать 99,9% времени и записи 0,1% от времени, не пытайтесь быть умным. Вы все равно должны войти в крит сек, даже когда вы только читаете. Это связано с тем, что вы не хотите, чтобы запись начиналась, когда вы находились в середине чтения.

6) Держите критические участки зернистыми. Каждый критический раздел должен защищать один ресурс, а не несколько ресурсов. Если вы делаете критические разделы слишком «большими», вы можете сериализовать свое приложение или создать очень таинственный набор тупиков или рас.

+0

Спасибо, Джон. Я обязательно буду помнить эти советы, особенно нет. 6. Я думаю, что я все еще что-то пропущу. Входит ли в критический раздел, чтобы другие потоки не обращались ко всем ресурсам перекрестных потоков или не выполнял 'EnterCriticalSection' /' LeaveCriticalSection' некоторую магию, которая препятствует доступу других потоков только к ресурсам, которые они инкапсулируют? Или существует способ связать ресурс с объектом CRITICAL_SECTION, который я пропустил полностью? –

+0

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

+0

@asveikau: Да, это было предназначено только для эмпирических правил, подходящих для нового многопроцессорного программиста. Кроме того, re: lock-writer locks (aka Gates), если чтение не долговечно, часто медленнее использовать Gate, чем просто использовать крит сек. См. Эту статью для получения дополнительной информации: http://queue.acm.org/detail.cfm?id=1454462 –

2

Используйте C++ обертка вокруг критической секции, которая поддерживает RAII:

{ 
    CriticalSectionLock lock (mutex_); 

    Do stuff... 
} 

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

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

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

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

+0

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

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