проблема спящего брадобрея
Oct. 27th, 2009 11:22 pmа вот это, наоборот, явно можно сделать проще, даже когда брадобреев много.
// вначале семафор закрыт
newCustomer = new AutoResetEvent(false);
// customer reception thread[s]
lock (queue) {
if (queue.Size == maxSize)
throw "нет мест";
queue.Enqueue(сustomer);
newCustomer.Set(); // open semaphore
}
// each barber's thread
while (true) {
newCustomer.WaitOne(); // let one barber through and close
lock (queue) {
if (queue.IsEmpty)
continue;
customer = queue.Dequeue();
}
Shave(customer);
}собственно, этот же код решает и проблему продюсеров-консъюмеров, вместо того, что там написано (на буфер памяти нет, а спящие треды копить память есть, да?)
(no subject)
Date: 2009-11-01 10:07 pm (UTC)(no subject)
Date: 2009-11-02 01:22 am (UTC)(no subject)
Date: 2009-11-02 09:28 am (UTC)По любому же получится, что в течение какого-то времени чувак тусуется в очереди хотя есть свободные барберы. Происходящего не от загрузки процессора, а от хреновости олгоритма.
Вообще его наверное можно исправить заменив этот эвент на семафор. Типа, на каждого поставленного в очередь кустомера ресепшен тред делает +1, а барберы лочатся в попытках сделать -1.
А так, по-моему, довольно явственно проявилось, что использование хитроумных локов для синхронизации самоподдерживается через Dunning–Kruger effect =)
(no subject)
Date: 2009-11-02 11:06 pm (UTC)с экспоненциальным бэкоффом. хотя ты прав в том смысле, что тогда получится не особо проще того, что в википедии написано.
> По любому же получится, что в течение какого-то времени чувак тусуется в очереди хотя есть свободные барберы.
это ты про когда два сразу пришли?
> Вообще его наверное можно исправить заменив этот эвент на семафор. Типа, на каждого поставленного в очередь кустомера ресепшен тред делает +1, а барберы лочатся в попытках сделать -1.
напиши, если не трудно, в явном виде?
(no subject)
Date: 2009-11-03 01:06 am (UTC)AutoResetEvent signal = new AutoResetEvent(false); object queueLock = new object(); Queue queue = new Queue(); Queue next = new Queue(); public void Next() { next.Enqueue(queue.Dequeue()); signal.Set(); } public void AcceptCustomer(T customer) { lock (queueLock) { if (queue.Count > 9000) throw new Exception("нет мест"); queue.Enqueue(customer); if (next.Count == 0) Next(); } } public void BarbersThread() { while (true) { signal.WaitOne(); // let one barber through and close T customer; lock (queueLock) { customer = next.Dequeue(); if (queue.Count > 0) Next(); } Shave(customer); } }any feedback will be greatly appreciated.
(no subject)
Date: 2009-11-03 08:59 pm (UTC)С этим можно бороться, например брадобреи могут делать так: while (true): lock, get customer, and signal.WaitOne() only if no customers. Не забыв отпустить лок конечно же =) Поскольку тред на ресепшене делает Set после того, как записывает очередного кастомера, всё сработает правильно (и этот Сет вообще говоря тоже нехило бы вынести из-под лока). Из недостатков -- ну, каждый брадобрей, ухвативший более одного кастомера с одного WaitOne, скорее всего стырил его у какого-то другого брадобрея, который по сигналу проснётся, залочит очередь и ничего там не обнаружит, это неэффективно.
Я предлагал другой способ: вместо AutoResetEvent используется Semaphore, ресепшен вызывает Release, барберы -- WaitOne. В отличие от мьютекса семафор не требует, чтобы релизил тот же тред, что лочил. В отличие от AutoResetEvent семафор не проёбывает число релизов. И вроде как довольно очевидно, что если у семафора установить тот же лимит, что и у очереди, то он не переполнится (а если и переполнится, то это не пройдёт бесследно, что ДИКО хорошо). Потому что ресепшен вначале пушит в очередь, потом в семафор, а барберы достают в обратном порядке.
Разумеется, ни в чём вышесказанном я не уверен и только массивные полевые испытания под жуткой нагрузкой могут немного этой уверенности прибавить.
PS: кстати, что за фигня с Reference Source (http://referencesource.microsoft.com/), не в курсе? Что-то оно похоже превратилось в зомби сразу после открытия (в смысле что за блогом, форумом и документацией никто не следит ваще), но вроде что-то иногда выкладывают (вот, под семёрку что-то выложили). И заставить его работать мне не удалось.
(no subject)
Date: 2009-11-03 10:38 pm (UTC)мля-а, ну кода же три строчки, что тут непонятного? я не верю, что ты посмотрел и не понял; я о тебе очень высокого мнения. давай ты посмотришь ещё раз, прежде чем я начну объяснять.
(no subject)
Date: 2009-11-03 10:50 pm (UTC)Меня вот что запутало: я же правильно понимаю, что в очереди next никогда не должно быть больше одного кастомера, то есть её можно смело превратить в `Customer next;` и вместо `if (next.count > 0)` писать `if (next != null)`, ну а вместо `next.Dequeue` -- `customer = next; next = null`?
Тогда да, кажется тоже работает.
(no subject)
Date: 2009-11-03 10:54 pm (UTC)(no subject)
Date: 2009-11-03 11:03 pm (UTC)и куда его совать-то, этот volatile? вместо локов не получится, локи всё равно нужны. а тогда лишний point of synchronization. но всё равно не понимаю, куда.
(no subject)
Date: 2009-11-03 11:17 pm (UTC)В принципе может быть это и не нужно в данном конкретном случае -- lock сам по себе вроде бы является read-write barrier (но вот насчёт read не уверен, кстати!), однако над такими вещами лучше не задумываться излишне, по-моему.
Не, это barberAwakeningPending имелось в виду!
(no subject)
Date: 2009-11-04 02:05 am (UTC)и да, lock - это всегда both read and write barrier, (иначе бы не работало ничего).
(no subject)
Date: 2009-11-03 11:00 pm (UTC)