Модераторы: Partizan, gambit
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> помогите разобраться с Queue и lock 
:(
    Опции темы
Гость_ser
Дата 1.2.2005, 05:26 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











два потока и стек типа FIFO: один запиcывает в буфер каждую ms. простой обект типа int, другой считывает из него. вроде Queue как нельзя лучше подходит для этого случая.
Код
public static Queue qBuffer = new Queue();
public static Queue qsyncBuf;
1-й поток:
Код
lock( qBuffer.SyncRoot ) {
   qBuffer.Enqueue( i );  
   qsyncBuf = Queue.Synchronized( qBuffer );      
}
2-й поток:
Код
int iDequeue;
...
lock( qBuffer.SyncRoot ) {

   qsyncBuf = Queue.Synchronized( qBuffer );

   if ( qsyncBuf.Count != 0 )
       iDequeue = (int)qsyncBuf.Dequeue();    
   else
       continue;      
}
из-за блокировки во втором потоке около половины значений не записываются в Queue в первом.
  Вверх
Domestic Cat
Дата 1.2.2005, 06:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 9
Всего: 172



Я не совсем понял чего ты хочешь сделать, подробнее плиз.


--------------------

PM   Вверх
Guest
Дата 1.2.2005, 06:18 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











в первом потоке я запиcываю в буфер Queue значение типа int.
во втором потоке считываю из этого буфера значение стоящее в очереди первым (FIFO).
как я понимаю после чтения посредством Dequeue это значение из очереди удаляется, что мне и надо.


  Вверх
Domestic Cat
Дата 1.2.2005, 06:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 9
Всего: 172



Тогда сразу для ФАКа - будьмо надеяться что добрые боги этого форума сжалятся :}

-----------------------------------------------------------------------------------------------------------

Синхронизация потоков по .NETовски

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

Основная проблема в том, что оба имеют дело с одним и тем же объектом - продуктом. Ели мы запустим 2 треда, н заботясь о синхронизации, то столкнемся с ситуацией когда потребитель попытается пользоваться товаром, который еще не произведен (в реале он повернется и уйдет, а у нас будет брошен нехороший иксепшн); или находится в процессе производства ( такое вообще представить трудно в жизни, в программе же баг такой можно долго ловить).

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

В .NET / Java дело происходит точно так же. Рассмотрим пример с производителем пива, и потребителем, который не хочет пить менее 10 ящиков пива за раз и требует чтобы пиво было подано в том порядке, в каком оно было произведено.

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

Вместо длинного размусоливания и подведения к логическому концу, приведу сразу решение :

Код

namespace Locks
{
class MainClass
{
 private Queue beer;
 private bool isBeerReady;

 public MainClass()
 {
  Console.WriteLine("************Start************");
  beer = new Queue(15);
  Thread producer = new Thread(new ThreadStart(Producer));
  Thread consumer = new Thread(new ThreadStart(Consumer));
  Random random = new Random();
  if (random.NextDouble() < 0.5)
  {
   producer.Start();
   consumer.Start();
   Console.WriteLine("Produсer makes, consumer waits");
  }
  else
  {
   consumer.Start();
   producer.Start();
   Console.WriteLine("Consumer waits, produсer makes");
  }
  Console.WriteLine("*************End*************");
 }

 [STAThread]
 static void Main(string[] args)
 {
  new MainClass();
 }

 public void Producer()
 {
  lock(beer.SyncRoot)
  {
   for (int i = 0; i < 10; i++)
   {
    Thread.Sleep(5);
    beer.Enqueue(i);
   }
   isBeerReady = true;
   Monitor.PulseAll(beer.SyncRoot);
   Console.WriteLine("Producer produced " + beer.Count + " packs of beer");
  }
 }

 public void Consumer()
 {
  lock(beer.SyncRoot)
  {
   while (! isBeerReady)
    Monitor.Wait(beer.SyncRoot);
   for (int i = 0; i < 10; i++)
   {
    Thread.Sleep(5);
    Console.WriteLine("Consumer is drinking " + beer.Dequeue() + "th pack of beer");
   }
  }
  Console.WriteLine("Consumer is good, very good.");
 }
}
}




Ну во-первых мы считаем, что для того, чтобы поизвести / выпить ящик пива требуется 5 мс.
Далее пойдем по решению.
В конструкторе создается 2 треда. Далее эти треды запускаются в случайном порядке.

Что делает Производитель.
1. Заходит в метод Producer.
2. Пытается забрать объект SyncRoot у beer. Тут возможны 2 ситуации:

2.а. Этот объект свободен (Потребитель еще не начал выполнять свой метод). Тогда Производитель забирает лок и блокирует Потребителю вход в Блок 2.
2.б. Этот объект занят (отобран Потребителем). Производитель ждет снаружи Блока 1.

Далее продолжаем оба варианта по отдельности, начинаем с 2.а.
--3.а. Производитель начинает производить пиво. Если теперь Потребитель попытается войти в Блок 2, он вынужден будет ждать снаружи, т.к. у него нет лока SyncRoot .

4.а. Производитель производит пиво, меняет флаг isBeerReady и вызывает Мonitor.PulseAll() (оба ничего в данном случае не делают), и высвобождает лок.

5.а. Истомленный жаждой Потребитель наконец получает лок, пропускает цикл и выпивает все.

--- 3.б. А что если первым лок получил Потребитель? С тредами ничего заранее не известно, даже если бы я не использовал рандом а четко указал какой тред запускается первым, все равно неизвестно как бы легла рука планировщика и какой бы слайс времени она бы отрезала каждому из тредов.
Итак, Потребитель получает лок. Но isBeerReady == false и Потребитель попадает в бесконечный цикл. А лок-то у него, и потому Производитель так бы и остался навечно ждатЬ доступа к локу, если бы не Monitor.Wait. Этот метод заставляет Потребителя отпустить лок и остановиться.

4.б. Теперь Производитель получает доступ к Блоку 1 и производит пиво. Перед самым завершением работы он уведомляет всех ждущих на объекте SyncRoot (то есть Потребителя) что усе готово - методом Monitor.PulseAll.

5. б. Потребитель просыпается и спокойно потребляет пиво.


--------------------

PM   Вверх
Guest
Дата 1.2.2005, 07:02 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











после того как закомментировал блокировку lock(...) во втором потоке, (все остальное оставил как есть) похоже что все работает без потерь (если не принимать во внимание CPU Usage=95% и готовность проц. блока на взлет от надрывной работы вентилятора процессора). Почему что-то работает, обяснить не могу, ибо скопировал куски из примеров майкрософта и подозреваю что перемудрил с синхронизацией на каждой итерации записи. Может есть механизм попроще?

  Вверх
Guest
Дата 1.2.2005, 07:04 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











извини, не заметил сообщения выше. сейчас почитаю, переварю
  Вверх
-ser-
Дата 1.2.2005, 11:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 344
Регистрация: 1.2.2005

Репутация: 5
Всего: 13



за пример спасибо, классика. но один важный вопрос для моего конкретного случая.
Цитата
2.б. Этот объект занят (отобран Потребителем). Производитель ждет снаружи Блока 1.

у меня старая добрая соц. система с 5-ти летним планом, для выполнения которого производитель обязан варить и отгружать пиво в три смены БЕЗ ОСТАНОВКИ независимо от поставщика. перепроизводство не его забота.
не получится ли в нашем случае так, что если машина заказчика со свежеотгруженным продуктом по каким-то причинам застряла на проходной на больше чем положенное время, то у нас встанет весь конвейер, сорвем пятилетку. Боюсь, что наивысший приоритет для Поставщика и наинизший для Заказчика не помогут.

Это сообщение отредактировал(а) -ser- - 1.2.2005, 11:11


--------------------
"Тонкая, однако, работа." 
PM MAIL ICQ   Вверх
Domestic Cat
Дата 1.2.2005, 11:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 9
Всего: 172



Цитата(ser @ 1.2.2005, 02:01)
не получится ли в нашем случае так, что если машина заказчика со свежеотгруженным продуктом по каким-то причинам застряла на проходной на больше чем положенное время, то у нас встанет весь конвейер, сорвем пятилетку.


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


--------------------

PM   Вверх
-ser-
Дата 1.2.2005, 11:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 344
Регистрация: 1.2.2005

Репутация: 5
Всего: 13



насколько я понял Wait потребителя для случая когда на складе пусто, а я имел ввиду задержку в цикле for Потребителя? может я что-то не понимаю. ладно буду пробовать. во всяком случае примерчик неплохой.


--------------------
"Тонкая, однако, работа." 
PM MAIL ICQ   Вверх
Domestic Cat
Дата 1.2.2005, 11:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 9
Всего: 172



Цитата(ser @ 1.2.2005, 02:20)
задержку в цикле for Потребителя?


Ааа ну так смо собой, если Производителю нужно работать с минимальными перерывами, то Потребитель бы просто упер себе все что тот произвел smile То есть, можно всю коллекцию клонировать а оригинал очистить. В данном случае результат которого я старался достичь - чтобы Потребитель получил не больше и не меньше 10 ящиков.
А для конкретной ситуации если что - пости, и там будем смотреть.


--------------------

PM   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Прежде чем создать тему, посмотрите сюда:
mr.DUDA
THandle

Используйте теги [code=csharp][/code] для подсветки кода. Используйтe чекбокс "транслит" если у Вас нет русских шрифтов.
Что делать если Вам помогли, но отблагодарить помощника плюсом в репутацию Вы не можете(не хватает сообщений)? Пишите сюда, или отправляйте репорт. Поставим :)
Так же не забывайте отмечать свой вопрос решенным, если он таковым является :)


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, mr.DUDA, THandle.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Общие вопросы по .NET и C# | Следующая тема »


 




[ Время генерации скрипта: 0.0495 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.