лабараторная работа. Теория_к_лабораторным. 1. Применение параллельных вычислений
Скачать 0.7 Mb.
|
5. Асинхронный вызов делегатаЕще одним очень простым в реализации способом выполнить что-либо в параллельном по отношению к основным вычислениям потоке является асинхронный вызов делегата. Для этого группируем код, который предполагается исполнять параллельно в отдельный метод, объявляем соответствующий по сигнатуре делегат, и присваиваем экземпляру этого делегата ссылку на наш метод. Оказывается, у делегата уже есть все необходимое, чтобы выполнить асинхронный вызов переданного ему метода, вернуть управление основному потоку, и после того, как основной поток завершит то, для чего ему управление было передано, дождаться окончания выполнения параллельных вычислений. Есть один нюанс: параллельные вычисления могут закончиться как позже основных, так и раньше. С первым случаем все понятно – мы дожидаемся их окончания вызовом метода 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(); } } } |