Реалізація архітектури “постачальник / споживач” в Visual C # (Sharp)

Методика постачальник / споживач не реалізована у вигляді типу, але застосуються в багатьох багатопоточних додатках Ідея архітектури постачальник / споживач полягає в розбитті проблеми на дві підпроблеми З одного боку нахітся постачальник даних, інформації і завдань Постачальник інкапсулює інфоацію у завдання, яке потрібно виконати Зі іншого боку знаходиться потрітель, відповідальний за вилучення даних і їх обробку

В інтерфейсі GUI Windows в багатопоточних додатках звертатися до компентам користувача інтерфейсу можуть тільки що створили їх потоки Щоб обійти цю проблему, застосовується метод invoke () бібліотеки windows Forms Для демонстрації використання цього методу ми створимо додатки GUI, в котом застосовується інший потік для періодичного збільшення значення лічильника, яке виводиться в текстовому полі Додаток створюється такий послідовниками кроків:

1 Створіть новий додаток Windows Forms і встановіть його стартовим прктом (клацніть правою кнопкою по імені проекту і виберіть опцію Set As StartUp Project)

2 Помістіть елемент управління TextBox на форму Forml

3 Виберіть елемент керування TextBox Якщо вікно Properties не відкрите, то клацніть правою кнопкою миші по елементу управління та виберіть команду Properties

4  Змініть властивість Name елемента керування TextBox на txtMessage

5 Клацніть правою кнопкою миші по формі і виберіть команду View Code

6 Додайте наступний код:

public partial class Forml: Form { public Forml() {

InitializeComponent()

}

private int _counter

private void IncrementCounter() {  txtMessageText = &quotCounter (&quot + _oounter + &quot)"

_counter++

}

delegate void DelegatelncremsntCounter() private void Periodiclncrement() {

while (1 == 1) {

Invoke(new DelegatelncremsntCounter(IncrementCounter)) ThreadSleep(1000)

}

}

Thread _thread

}

7 Перейдіть назад в подання форми і двічі клацніть мищью за формою Це відкриє код методу Formi_Load ()

8 Додайте наступний код в код методу Formi_Load ():

private void Forml_Load(object sender, EventArgs e) {

„thread = new Thread(new ThreadStart(Periodiclncrement))

_threadstart()

&gt&nbsp

Метод Formi_Load () виповнюється при завантаженні форми Formi і створює новий пок, який потім виконує метод Periodiclncrement () У реалізації методу Periodiclncrement () є нескінченний цикл, що викликає метод Form, invoke (), якому передається делегат Делегат є методом incrementcounter (), який збільшує лічильник і виводить результат у текстом поле txtMessage

З точки зору користувача, здавалося б, очевидним викликати метод incrementcounter () безпосередньо з іншого потоку (_thread) Але реалізація методу invoked приховує реалізацію постачальника / споживача Постачальником яяется метод invoke (), який додає делегата, якого необхідно поставити в чергу А споживачем є клас windows Forms Form, який періодічкі перевіряє чергу методу invoke () і виконує знаходяться в ній делегатів

Коротенько, реалізація постачальник / споживач є чимось більшим, ніж перача інформації від одного потоку іншому Цей процес є ефективним, т к постачальник і споживач розділені, і кожен займається власними дами Єдиною загальною точкою між постачальником і споживачем є черга, або список, яка синхронізується і містить інформацію, претавляющую інтерес як для постачальника, так і для споживача

Архітектура, реалізована windows Forms, елегантна і самодостатня За зразком моделі invoke о можна реалізувати загальну архітектуру постачальника / потребеля, як показано в наступному коді:

interface IProducerConsumer { void Invoke(Delegate Sdelegate)

void Invoke(Delegate Sdelegate, Object[] arguments)

}

class ThreadPoolProducerConsumer : IProducerConsumer { class Executor {

public readonly Delegate „delegate

public readonly Object[] „arguments

public Executor(Delegate ©delegate, Object[] arguments) {

„delegate = Sdelegate

„arguments = arguments

&gt&nbsp

}

private Queue&lt Executor&gt „queue = new Queue&ltExecutor&gt()

private void QueueProcessor(Object obj) { MonitorEnter(„queue)

while („queueCount == 0) { MonitorWait(„queue, -1)

}

Executor exec = „queueDequeue() MonitorExit(„queue)

ThreadPoolQueueUserWorkItem(new WaitCallback(QueueProcessor)) exec„delegateDynamiclnvoke(exec„arguments)

}

public SingleThreaderProducerConsumer() { ThreadPoolQueueUserWorkltem(new WaitCallback(QueueProcessor))

}

public void Invoke(Delegate (Sdelegate, Object[] arguments) { MonitorEnter(„queue)

„queueEnqueue(new Executor(@delegate, arguments)) MonitorPulse(„queue)

MonitorExit(„queue)

}

}

Клас з ThreadPoolProducerConsumer імее т ОДИН общи ї мето д Invoke (), яка й іспользуетс я подібне про метод у invoke () бібліотек і windowsForms Чт про Робіть т об – щі х постачальника / споживче я працездатними , Та к пов про применени е Сінхронізуется – ціонног про клас а Monitor

Щоб и зрозуміти, каки м образо м клас з Monitor работае т в контекст е постав – щик / споживач , Розглянь м реализаци ю постачальник / споживач в цілому За – то до споживачів я (QueueProcessor ()) виполняетс я ПОСТІЙНО, ожида я елемент и и з очеред і (queue) Дл я перевіркою і очеред і визиваетс я мето д MonitorEnter (), кото – рій, по суті, говорить: Я хоч у виняткові й контрол ь на д блоко м коду, який ї заканчіваетс я викличу м метод а MonitorExit о . Дл я перевіркою і очеред і

запускається цикл while Цикл очікує до тих пір, поки в черзі немає елементу для обробки Даний потік міг би виконуватися постійно, очікуючи додавання елементів в чергу, але поки він виконує цикл, він утримує блокування А це означає, що потік постачальника не може додати нічого в чергу

Таким чином, споживач повинен звільнити блокування, щоб в чергу можна було додати елементи, але в той же самий час він повинен перевіряти очедь на наявність в ній елементів для обробки Виходом з цієї ситуації являея використання методу Monitorwait (), який змушує потік споживача звільнити блокування і заявити: Я тимчасово звільняю блокування до тих пір, поки мені не буде дано сигнал продовжити обробку . Звільнивши блокування, пок споживача переходить в режим сну і чекає сигнал для пробудження

Потік постачальника (invoke ()) також входить в захищений блок, використовуючи метод Monitor Enter () Усередині захищеного блоку коду він додає елемент в чергу за допомогою методу Enqueue () Так як в чергу був доданий елемент, то потооставщік за допомогою методу Monitor Pulse про посилає сигнал, який вказує наявність елементів в черзі Це змусить потік, який тимчасово звільнив блокування (потік-споживач), вийти з режиму сну Але потік-споживач волняется тільки після виклику потоком-постачальником методу MonitorExito До тих пір потік-споживач знаходиться в режимі готовності до виконання

У простому випадку даної реалізації одиночний потік постійно виконував б метод QueueProcessor () Реалізацію можнаоптимізувати, створившиівикористовуючи пул потоків Пул потоків представляє собою колекцію готових до виконанню потоків За міру прибуття задач потоки беруться з пулу і застосовуються для виконання завданьЗазакінчення виконаннязавданняпотік повертаєтьсяпроатно У пул потоків У конструкторі ThreadPoolProducerConsumer метод ThreadPool QueueUserWorkItem () Використовує підхід пулу ПОТОКІВ ДЛЯ виконання методу QueueProcessorо У реалізації методуQueueProcessor() Метод ThreadPool Queueuserworkitem()  викликається зновупередвикликом делегата У результаті один потік завжди чекає на елемент в черзі, але елементи з черзі можуть оброблятися декількома потоками одночасно

Використання загального постачальника / споживача майже ідентично застосуванню мода invoke () бібліотеки windowsForms Далі наводиться приклад його реалізації:

public class TestProducerConsumer { delegate void TestMethodO

void Methodf) {

ConsoleWriteLine(&quotProcessed in thread id (&quot +

ThreadCurrentThreadManagedThreadld + &quot)&quot)

}

public void TestSimpleO {

IProducerConsumer producer = new ThreadPoolProducerConsumer()

ConsoleWriteLine(&quotSent in thread id (&quot +

ThreadCurrentThreadManagedThreadld + &quot)&quot) producerInvoke(new TestMethod(Method))

}

}

Метод TestSimple () створює екземпляр типу ThreadPoolProducerConsumer ПОТІМ за допомогою делегата TestMethod, виконуючого метод Method (), викликається метод invoke () У windows Forms створюється екземпляр іншого типу, але використовується той же самий метод invoke () Реалізація також трохи відрізняється тим, що потребель є не одним потоком, а декількома потоками в необхідній кількості

Джерело: Гросс К С # 2008: Пер з англ – СПб: БХВ-Петербург, 2009 – 576 е: ил – (Самовчитель)

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*