Public voidMyThread_()

{ for (inti = 1; i<= 10; i++)

{ myWait.WaitOne();                                             //ожиданиеобъекта-события

for (int j = 0; j < 100000; j++) sum++;

myWait.Reset();                                                            //освобождениеобъекта-события

Console.WriteLine("Поток {0} sum = {1,-7} время = {2,-3} итерация = {3} ",

                                          Thread.CurrentThread.ManagedThreadId,

sum,

Environment.TickCount - myTime,i);

Thread.Sleep(5);

}

}

Public void Test_()

{sum = 0;                                                                                      //общийразделяемыйресурс                      

myTime = Environment.TickCount;

Thread[] tt = { new Thread(MyThread_), new Thread(MyThread_), new Thread(MyThread_) };

for (inti = 0; i< 3; i++) tt[i].Start();                                       // запускпотоков

while (true)

{          myWait.Set();                                                                 //объект переведен в состояние “сигнализрует”

           boolttt = tt[0].IsAlive || tt[1].IsAlive || tt[2].IsAlive;

i f (!ttt) break;                                                        // выход, если все потоки “не живы ”                        

           Thread.Sleep(5);

}

// for (inti = 0; i< 3; i++) tt[i].Join();

Console.WriteLine("sum = {0}, времявыполнения = {1} ", sum, Environment.TickCount-myTime);

}

Результат работы

Поток 3 sum = 100000 время = 0 итерация = 1

Поток 4 sum = 200000 время = 16 итерация = 1

Поток 5 sum = 300000 время = 31 итерация = 1

Поток 3 sum = 400000 время = 47 итерация = 2

Поток 4 sum = 500000 время = 63 итерация = 2

...

Поток 4 sum = 2600000 время = 391 итерация = 9

Поток 5 sum = 2700000 время = 406 итерация = 9

Поток 3 sum = 2800000 время = 422 итерация = 10

Поток 4 sum = 2900000 время = 438 итерация = 10

sum = 2900878, время выполнения = 453

 

Поток 5 sum = 3000000 время = 453 итерация = 10

Для продолжения нажмите любую клавишу...

 

Результат получен правильный, но следует иметь в виду, что оператор Console.Writeline(….) исполняет потоковую операцию вывода, т.е. порождает новый поток, который исполняется параллельно с существующими. Так как для консольного оператора в главном потоке имеются все выводимые значения (пусть даже не в окончательном варианте), то оператор готов к выполнению и может быть выполнен раньше вывода в дочернем потоке, который формирует и выводит окончательные значения результатов. Поэтому целесообразно оставить операторы ожидания завершения потоков, т.е. for (inti = 0; i< 3; i++) tt[i].Join();

 

Результат работы в этом случае (конец вывода)

...

Поток 4 sum = 2700000 время = 407 итерация = 9

Поток 3 sum = 2800000 время = 422 итерация = 10

Поток 5 sum = 2900000 время = 438 итерация = 10

Поток 4 sum = 3000000 время = 453 итерация = 10

sum = 3000000, время выполнения = 469

Для продолжения нажмите любую клавишу...

 

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

Задача “Поставщики - потребители”

Суть задачи сводится к следующему: имеется общий разделяемый ресурс, в который поставщики записывают информацию, а потребители читают. Объем разделяемого ресурс может допускать запись нескольких единиц информации (нескольких сообщений от поставщиков) или только одной. В рассматриваемом примере ресурс будет содержать одно сообщение.

В качестве общего разделяемого ресурса удобно использовать объект класса-шаблона Queue<T>, который предоставляет удобные свойства и методы:

Count – текущее число элементов в очереди,

Enqueue() – добавление сообщения в конец очереди,

Dequeue() – извлечение с удалением начального элемента очереди.

 

Частный случай: один или несколько поставщиков, 1 потребитель. Данный случай определяет ограничения на поведение участников процесса:

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

чтение сообщения возможно только при наличии сообщений в очереди (ресурс не пуст).

 

Class SupNCons1

{        objectlock_obj = new object();                          // объектблокировки      

Queue<string>Qchange = new Queue<string>();

           int N1_sup = 5;                                           // число сообщений для каждого поставщика

           intNmsg = 5;                          //общее число сообщений для потребителя

voidSup(objectnum)                                                    // метод для потока поставщика

{ string n = Convert.ToString(num);                  // аргумент -№ поставщика

Randomr = newRandom();                                 //объект используется для формирования случайного

                                                                                    //значениязадержки       

for (inti = 0; i< N1_sup; i++)

{ lock (lock_obj) { Qchange.Enqueue("сообщение №" + (i + 1).ToString() + " от " + n);

                                                                                         // блокировка и запись сообщения в очередь        

 }

intdel = Convert.ToInt32(r.NextDouble()) * 3 + 1;      //преобразование вещ. в целое от1 до 3 

Thread.Sleep(del);                                                                   //Задержка потока

}

}

Void Cons()

{ stringstr = "";

intnread = 0;

boolt;

while (nread<Nmsg)                                                                   //цикл чтения сообщений

{         t = false;                                                                            

           lock (lock_obj)                                                           // блокировка общего ресурса

           { if (Qchange.Count> 0) { str = Qchange.Dequeue(); t = true; }

// чтение и удаление первого сообщения из очереди

           }                                                                                       // еслиресурснепуст

           if (t == false) { Thread.Sleep(50);                                       // задержка, еслиресурспуст

                                 }

           else  { Console.WriteLine(str);

                          nread++;// изменение счетчика прочитанных сообщений

                          }

}

Public void Test()

{         Thread ts1 = new Thread(Sup);

           Thread ts2 = new Thread(Sup);

          N1_sup = 10;

           Nmsg = 2 * 10;

           Thread tc = new Thread(Cons);

           ts2.Start(2);

           tc.Start();

           ts1.Start(1);

}

}                                                                                                       // конец класса SupNCons1

Результат выполнения

сообщение №1 от 2

сообщение №1 от 1

сообщение №2 от 1

сообщение №2 от 2

сообщение №3 от 2

сообщение №3 от 1

сообщение №4 от 2

сообщение №4 от 1

сообщение №5 от 1

сообщение №5 от 2

сообщение №6 от 2

сообщение №6 от 1

сообщение №7 от 1

сообщение №7 от 2

сообщение №8 от 2

сообщение №9 от 2

сообщение №8 от 1

сообщение №9 от 1

сообщение №10 от 2

сообщение №10 от 1                          

Для продолжения нажмите любую клавишу...

 

Вариант 2: N поставщиков, M потребителей. Требуется, чтобы каждый потребитель прочитал все сообщения от всех поставщиков. На поведение участников процесса накладываются ограничения:

· Доступ к ресурсу должен быть монопольным.

· Сообщение может быть удалено из общего ресурса, при условии, что оно прочитано всеми потребителями.

Следует отметить также, что поток потребителя может многократно обращаться к ресурсу и должен уметь распознать, является ли текущее сообщение новым для него или уже прочитанным. Таким образом, число обращений потребителя к ресурсу обычно больше, чем NS * Nmsg, где* NS - число поставщиков, Nmsg - число поставок от каждого поставщика.

 

Общий разделяемый ресурс в многопоточных приложениях часто оформляют в виде структур или классов. Класс Queue<string> в данном случае не обеспечивает выполнения 2-го ограничения, т.к. метод Dequeue() сразу удаляет элемент из очереди. Данная проблема решается с помощью наследования:

 

classMyQueue:Queue<string>

{         staticstringlast_res = "";                         //текст очередного сообщения, которое должны

                                                                                         // прочитать все потребители

           publicintNc = 0;                                          //число всех потребителей

           staticintNc_read = 0;              //число потребителей, прочитавших текущее сообщение

 


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: