Наша взаимовыгодная связь https://banwar.org/
[Назад] [Наступна сторінка]
Ще одним дуже простим в реалізації способом виконати що-небудь в паралельному по відношенню до основних обчислень потоці є асинхронний виклик делегата. Для цього групуємо код, який передбачається виконувати паралельно в окремий метод, оголошуємо відповідний по сигнатурі делегат, і присвоюємо примірнику цього делегата посилання на наш метод. Виявляється, у делегата вже є все необхідне, щоб виконати асинхронний виклик переданого йому методу, повернути управління основному потоку, і після того, як основний потік завершить то, для чого йому управління було передано, дочекатися закінчення виконання паралельних обчислень.
Є один нюанс: паралельні обчислення можуть закінчитися як пізніше основних, так і раніше. З першим випадком все зрозуміло - ми чекаємо їх закінчення викликом методу EndEnvoke (). У другому випадку паралельний потік може повідомити про своє дострокове завершення за допомогою зворотного виклику. У наведеному прикладі метод MyReport () викликається паралельно. Методу BeginEnvoke () делегата, який здійснює асинхронний виклик MyReport (), крім параметра IterationsCount, передається покажчик на метод MyReportCompleted (), який і буде викликаний по закінченню "формування звіту". Важливо врахувати, що метод зворотного виклику буде викликаний в паралельному потоці, а значить потрібно бути акуратним при оновленні елементів призначеного для користувача інтерфейсу і діяти за аналогією з попереднім кроком : Використовувати властивість InvokeRequired і метод Invoke ().
Приклад роботи програми з асинхронним викликом делегата.
// Приклад 3. Асинхронний виклик делегата // Оголошення делегата private delegate void MyReportDelegate (int _IterationsCount); // Виклик асинхронного делегата private void btnASDInvoke_Click (object sender, EventArgs e) {int _IterationsCount = Convert. ToInt32 (numericUpDownWork .Value); // Початок обробки в паралельному потоці MyReportDelegate _invoke = MyReport; // Як параметр передається метод, який буде викликаний по закінченню роботи MyReport IAsyncResult _invokeResult = _invoke. BeginInvoke (_IterationsCount, MyReportCompleted, this); // 'Обнулення' протоколу this. ASDLog = string. Empty; // Паралельна MyReport обробка ... for (int i = 0; i <_IterationsCount; i ++) {this. ASDLog = this. ASDLog + "Інша робота ..." + Math. Round ((100 * (double) (i + 1) / _IterationsCount), 2). ToString () + "%" + Environment .NewLine; Thread. Sleep (10); } This. ASDLog = this. ASDLog + "Інша робота завершена." + Environment .NewLine; // Чекаємо закінчення обробки MyReport _invoke. EndInvoke (_invokeResult); // Висновок протоколу обробки textBoxASDInvokeLog .Text = this. ASDLog + "Всі роботи завершені." + Environment .NewLine; } // Формування звіту private void MyReport (int _IterationsCount) {for (int i = 0; i <_IterationsCount; i ++) {this. ASDLog = this. ASDLog + "Формування звіту ..." + Math. Round ((100 * (double) (i + 1) / _IterationsCount), 2). ToString () + "%" + Environment .NewLine; Thread. Sleep (10); }} // Завершення формування звіту private void MyReportCompleted (object state) {textBoxASDInvokeLog .Text = textBoxASDInvokeLog .Text + "!" ; this. ASDLog = this. ASDLog + "Формування звіту завершено." + Environment .NewLine; }
І ще один момент. Нижче наведено фрагмент коду, який реалізує властивість ASDLog, доступ до якого необхідно розділяти між потоками. Розділяється ресурс, в першу чергу, необхідно захистити від одночасної коригування декількома потоками, а в наданні доступу на читання кількох потокам відразу немає нічого кримінального. Але, якщо доступ на читання взагалі ніяк не обмежувати, то може трапитися ситуація, коли під час зміни ресурсу хтось спробує його прочитати. Результат цієї ситуації непередбачуваний, і все залежить від природи ресурсу. Екземпляри класу ReaderWriterLockSlim допомагають вирішити завдання оптимізації потокобезпечна доступу до ресурсу: вони дозволяють його читати кільком потокам відразу, але, якщо один з потоків отримав доступ на редагування, то все "читачі" на час редагування блокуються.
// Протокол обробки private string m_ASDLog = string. Empty; // потокобезпечна блокування, оптимізована для читання private ReaderWriterLockSlim m_ASDLogLock = new ReaderWriterLockSlim (); // Властивість "Протокол обробки", що використовує ReaderWriterLockSlim internal string ASDLog {get {m_ASDLogLock. EnterReadLock (); try {return m_ASDLog; } Finally {m_ASDLogLock. ExitReadLock (); }} Set {m_ASDLogLock. EnterWriteLock (); try {m_ASDLog = value; } Finally {m_ASDLogLock. ExitWriteLock (); }}}
[Назад] [Наступна сторінка]