Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Базовые технологии платформы .NET.pdf
Скачиваний:
65
Добавлен:
11.05.2015
Размер:
1.81 Mб
Скачать

BlockingCollection<int> bc = new BlockingCollection<int>(); for (int producer = 0; producer < 10; producer++)

{

Task.Factory.StartNew(() =>

{

Random rand = new Random(); for (int i = 0; i < 5; i++)

{

Thread.Sleep(200); bc.Add(rand.Next(100));

}

});

}

var consumer = Task.Factory.StartNew(() =>

{

foreach (var item in bc.GetConsumingEnumerable())

{

Console.WriteLine(item);

}

});

consumer.Wait();

Классы ConcurrentQueue<T>, ConcurrentStack<T>, ConcurrentBag<T> и

ConcurrentDictionary<T> – это потокобезопасные классы для представления очереди, стека, неупорядоченного набора объектов и словаря. Предполагается, что данные классы будут использоваться в качестве ресурсов, разделяемых между потоками, вместо обычных классов-коллекций. Отличительная особенность данных коллекций – наличие Try-методов для получения (изменения) элементов. Такие методы удобны, так как исключают предварительные проверки существования и необходимость использования в клиентском коде секции lock.

// используем объект типа ConcurrentStack<T> T item;

if (concurrentStack.TryPop(out item)) // пытаемся извлечь элемент

{

UseData(item);

}

35. Асинхронный вызов методов

Платформа .NET содержит средства для поддержки асинхронного вызова методов. При асинхронном вызове поток выполнения разделяется на две части: в одной выполняется метод, а в другой – процесс программы. Асинхронный вызов служит альтернативой использованию многопоточности явным образом1.

1 Подход, описанный в данном параграфе, является старой моделью организации параллельных вычислений. Вместо работы с асинхронными методами следует применять выполнение асинхронных операций при помощи задач.

150

Асинхронный вызов всегда выполняется посредством объекта некоторого делегата. Любой такой объект содержит два специальных метода для асинхронных вызовов – BeginInvoke() и EndInvoke(). Эти методы генерируются во время выполнения программы, так как их сигнатура зависит от делегата. Внимание: объект группового делегата нельзя вызвать асинхронно.

Метод BeginInvoke() обеспечивает асинхронный запуск. Кроме параметров, указанных при описании делегата, метод BeginInvoke() имеет два дополнительных параметра. Первый дополнительный параметр указывает на функцию завершения, выполняемую после окончания асинхронного метода. Второй дополнительный параметр – это объект, при помощи которого функции завершения может быть передана некоторая информация. Метод BeginInvoke() возвращает объект, реализующий интерфейс IAsyncResult. При помощи этого объекта становится возможным различать асинхронные вызовы одного и того же метода.

Приведём описание интерфейса IAsyncResult:

interface IAsyncResult

{

object AsyncState{ get; } WaitHandle AsyncWaitHandle{ get; } bool CompletedSynchronously{ get; } bool IsCompleted{ get; }

}

Поле IsCompleted позволяет узнать, завершилась ли работа асинхронного метода. В поле AsyncWaitHandle хранится объект типа WaitHandle. Программист может вызывать методы объекта WaitHandle для контроля над потоком выполнения асинхронного метода. Объект AsyncState хранит последний аргумент, указанный при вызове BeginInvoke().

Делегат для функции завершения описан следующим образом:

public delegate void AsyncCallback(IAsyncResult ar);

Как видим, функции завершения передаётся единственный аргумент – объект, реализующий интерфейс IAsyncResult.

Рассмотрим пример асинхронного вызова метода, который вычисляет и печатает факториал целого числа. Ни функции завершения, ни возвращаемое методом BeginInvoke() значение не используются. Подобный подход при работе с асинхронными методами называется «выстрелил и забыл» (fire and forget).

//создадим лямбду, чтобы вычислять факториал

Func<uint, BigInteger> factorial = null;

factorial = n => (n == 0) ? 1 : n * factorial(n - 1);

//создадим лямбду, чтобы печатать факториал

Action<uint> print = n => Console.WriteLine(factorial(n));

// запустим метод асинхронно, игнорируя дополнительные параметры print.BeginInvoke(8000, null, null);

151

// эмулируем работу (факториал увидим где-то на третьей итерации) for (int i = 1; i < 10; i++)

{

Console.Write("Do some work..."); Thread.Sleep(3000);

}

Модифицируем предыдущий пример. Будем передавать в BeginInvoke() функцию завершения и дополнительный аргумент.

//объект print не изменился

//функция завершения будет выводить время выполнения метода

AsyncCallback timer = ar =>

{

var dt = (DateTime) ar.AsyncState; Console.WriteLine(DateTime.Now - dt);

}; print.BeginInvoke(8000, timer, DateTime.Now); print.BeginInvoke(1000, timer, DateTime.Now);

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

Используем метод EndInvoke() при вычислении и печати факториала:

//объект factorial определён в первом примере

//так как отслеживаем окончание работы методов,

//сохраняем результат вызова BeginInvoke()

IAsyncResult ar1 = factorial.BeginInvoke(8000, null, null); IAsyncResult ar2 = factorial.BeginInvoke(1000, null, null); Thread.Sleep(2000);

//получаем результат второго вызова и печатаем его

BigInteger res1 = factorial.EndInvoke(ar2); Console.WriteLine(res1);

//получаем и печатаем результат первого вызова

BigInteger res2 = factorial.EndInvoke(ar1); Console.WriteLine(res2);

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

152

Литература

1.Албахари, Дж. C# 5.0. Справочник. Полное описание языка: Пер. с англ. / Дж. Албахари, Б. Албахари. – 5-е изд. – М.: ООО «И.Д. Вильямс», 2013. – 1008 с.: ил.

2.Нэш, Т. C# 2010: ускоренный курс для профессионалов / Т. Нэш. – М. : Издательский дом «Вильямс», 2010. – 592 с.

3.Троелсен, Э. Язык программирования C# 5.0 и платформа .NET 4.5 / Э. Троелсен. – 6-е изд. – М.: ООО «И.Д. Вильямс», 2013. – 1312 с.: ил.

4.Рихтер, Дж. CLR via C#. Программирование на платформе Microsoft .NET Framework 4.5 на языке C# / Дж. Рихтер. – 4-е изд. – Спб.: Питер, 2013. – 896 с.:

ил.

5.Фримен, А. LINQ: язык интегрированных запросов в C# 2010 для профессионалов / А. Фримен, Дж. С. Раттц-мл. – М. : Издательский дом «Вильямс»,

2011. – 656 с.

6.Хейлсберг, А. Язык программирования C#. Классика Computers Science. / А. Хейлсберг, М. Торгерсен, С. Вилтамут, П. Голд. – 4-е изд. – Спб.: Питер, 2012.

784 с.: ил.

7.Цвалина, К. Инфраструктура программных проектов: соглашения, идиомы и шаблоны для многократно используемых библиотек .NET. : Пер. с англ. / К. Цвалина. – М.: ООО «И.Д. Вильямс», 2011. – 416 с.: ил.

153