Upload
cs-center
View
303
Download
0
Embed Size (px)
Citation preview
Толстиков Никита
Многопоточное программирование
15.04.2015 1Многопоточность
План лекции
• APM
• EAP
• TAP
• Concurrent collections
15.04.2015 Толстиков Никита 2Многопоточность
Асинхронное программирование
• Основные паттерны:
– Asynchronous Programming Model (APM)
– Event-based Asynchronous Pattern (EAP)
– Task-based Asynchronous Pattern (TAP)
15.04.2015 Толстиков Никита 3Многопоточность
Asynchronous Programming Model
• Использует асинхронные делегаты и
IAsyncResult объекты
• Основная идея использовать два метода:
– BeginOperationName – для старта
асинхронной операции
– EndOperationName – для получения
результатов
• Использовалась в legacy системах до 4.0 .Net
• Depricated
15.04.2015 Толстиков Никита 4Многопоточность
Asynchronous Programming Model
15.04.2015 Толстиков Никита 5Многопоточность
public class AsynPrimeCalcer{
private delegate int GetPrimeCountHandler(int min, int count);private static GetPrimeCountHandler ourGetPrimeCountCaller = GetPrimeCount;
public IAsyncResult BeginGetPrimeCount(int min,int count, AsyncCallback callback, object userState)
{Console.WriteLine("EndGetPrimeCount on {0}",
Thread.CurrentThread.ManagedThreadId);return ourGetPrimeCountCaller.BeginInvoke(min, count, callback, userState);
}
public int EndGetPrimeCount(IAsyncResult result){
Console.WriteLine("EndGetPrimeCount on {0}", Thread.CurrentThread.ManagedThreadId);
return ourGetPrimeCountCaller.EndInvoke(result);}
private static int GetPrimeCount(int min, int count){
return ParallelEnumerable.Range(min, count).Count(n =>Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i =>n % i > 0));
}}
Asynchronous Programming Model
15.04.2015 Толстиков Никита 6Многопоточность
public static void PrimeCounterExample(){
var asyncCalcer = new Slides.AsynPrimeCalcer();var res1 = asyncCalcer.BeginGetPrimeCount(1, 1000, PrintResults, asyncCalcer);var res2 = asyncCalcer.BeginGetPrimeCount(1000, 2000, PrintResults, asyncCalcer);var res3 = asyncCalcer.BeginGetPrimeCount(3000, 40000, null, null);
res3.AsyncWaitHandle.WaitOne();Console.WriteLine(asyncCalcer.EndGetPrimeCount(res3));
}
public static void PrintResults(IAsyncResult asyncResult){
Slides.AsynPrimeCalcer calcer = (Slides.AsynPrimeCalcer) asyncResult.AsyncState;Console.WriteLine(calcer.EndGetPrimeCount(asyncResult));
}
Event-based Asynchronous Pattern
• Использует асинхронные события и их
обработчики
• Основная идея использовать 3 типа методов в
классе:
– MethodNameAsync– для старта асинхронной
операции
– MethodNameCompletedEvent – событие
завершения операции
– MethodNameCancel– для остановки операции
• Depricated
15.04.2015 Толстиков Никита 7Многопоточность
События
• Это механизм класса сообщать его
пользователю, что слчуилось что то
интересное
• Пользователь сам может решать что ему
интерсно – подписываться на события
• Такая модель взаимодействия
называется publisher-subscriber
15.04.2015 Толстиков Никита 8Многопоточность
События
• В С# события – это специальные
объекты, которые при сробатываении
запускает все подписанные на себя
делигаты
• Cобытия вводятся при помощи ключевого
слова event
15.04.2015 Толстиков Никита 9Многопоточность
Пример
15.04.2015 Толстиков Никита 10Многопоточность
public static event EventHandler _show;
static void Main(){
// Add event handlers to Show event._show += new EventHandler(Dog);_show += new EventHandler(Cat);_show += new EventHandler(Mouse);_show += new EventHandler(Mouse);
// Invoke the event._show.Invoke();
}static void Cat(){
Console.WriteLine("Cat");}static void Dog(){
Console.WriteLine("Dog");}static void Mouse(){
Console.WriteLine("Mouse");}
Event-based Asynchronous
15.04.2015 Толстиков Никита 11Многопоточность
public class AsyncExample{
public class Method1CompletedEventHandlerArgs : EventArgs{}public delegate void Method1CompletedEventHandler(object sender,
Method1CompletedEventHandlerArgs args);// Synchronous methods.public int Method1(string param);public void Method2(double param);
// Asynchronous methods.public void Method1Async(string param);public event Method1CompletedEventHandler Method1Completed;
public void Method2Async(double param);public void Method2Async(double param, object userState);public event Method2CompletedEventHandler Method2Completed;
public void CancelAsync(object userState);
public bool IsBusy { get; }
// Class implementation not shown.}
Event-based Asynchronous Pattern
• Перегрузка методов с использованием
userState позволяет вызывать Async операции
не дожидаясь выполнения предыдущих
• CancelAsync – позволяет остановить
запущенную операцию по userStata
• WebClient и BackgroundWorker представляют
собой наиболее распространненые примеры
EAP
15.04.2015 Толстиков Никита 12Многопоточность
Реализация
здесь
15.04.2015 Толстиков Никита 13Многопоточность
Task-based Asynchronous Pattern
• Использует Task’и и PFX
• Для обозначения так же используется суффикс Async
• Каждый метод должен возвращать System.Threading.Tasks.Task
15.04.2015 Толстиков Никита 14Многопоточность
public static Task<int> ReadTask(Stream stream, byte[] buffer, int offset, int count, object state)
{var tcs = new TaskCompletionSource<int>();stream.BeginRead(buffer, offset, count, ar =>{
try { tcs.SetResult(stream.EndRead(ar)); }catch (Exception exc) { tcs.SetException(exc); }
}, state);return tcs.Task;
}
Реализация
• Ручная– Метод, который возвращает Task или
Task<TResult>
– Программист сам обрабатывает исключения
• Компилятор– Метод, который возвращает значение, но помечен
словом async
– Компилятор сам генерирует код обработки ошибок и
оборачивания в Task
• Гибридная
15.04.2015 Толстиков Никита 15Многопоточность
Ручная
• Все задачи создаются в ручную: Task.Run()
• Завершение задач ожидается в ручную
• Исключения выбрасываются в месте, где
вызывается свойство Result, метода Wait() или
Task.WaitAll()/Task.WaitAny()
• Все исключения из Task обернуты в
AggregationException
15.04.2015 Толстиков Никита 16Многопоточность
Ручная
15.04.2015 Толстиков Никита 17Многопоточность
public class TaskPrimeCalcer{public delegate Task GetPrimeCountEventHandler(object sender, GetPrimeEventArg e);public event GetPrimeCountEventHandler GetPrimeCountClick;private PrimeCalcer myPrimeCalcer = new PrimeCalcer();
public Task Click(int min, int count){
return GetPrimeCountClick.Invoke(this, new GetPrimeEventArg {Count = count, Minimum = min});
}
public Task<int> Calc(int min, int count){
return Task.Run(() => myPrimeCalcer.GetCount(min, count));}
public Task<int> CalcAsync(int min, int count){
return Task.Run(() => myPrimeCalcer.GetCountMultithread(min, count));}
}
Ручная
15.04.2015 Толстиков Никита 18Многопоточность
public static Task ButtonClick_Event(object sender, Slides.GetPrimeEventArg e){
return Task.Run(() =>{
var primeCalcer = (Slides.TaskPrimeCalcer) sender;return primeCalcer.Calc(e.Minimum, e.Count).Result;
}).ContinueWith(task => Console.WriteLine("From: {0} {1} numbers - {2}", e.Minimum, e.Count, task.Result));
}
...
var taskPrime = new Slides.TaskPrimeCalcer();taskPrime.GetPrimeCountClick += ButtonClick_Event;
using (new OperationTimer("Async tasks")){
Task.WaitAll(taskPrime.Click(1, 10000000), taskPrime.Click(10000000, 10000000), taskPrime.Click(20000000, 10000000));
}
Компилятор
• Все задачи создаются в ручную : Task.Run()
• Вводится при помощи ключевых слов async/await
• await – асинхронное ожидание, не
блокирует поток, а ставит его на паузу
• Исключение выбрасывается в await
• Все исключения перебрасываются в
первозданном виде15.04.2015 Толстиков Никита 19Многопоточность
Компилятор
15.04.2015 Толстиков Никита 20Многопоточность
public static async Task ButtonClick_EventAsync(object sender, Slides.GetPrimeEventArg e){
var primeCalcer = (Slides.TaskPrimeCalcer)sender;int result = await primeCalcer.Calc(e.Minimum, e.Count);Console.WriteLine("From: {0} {1} numbers - {2}", e.Minimum, e.Count, result);
}...
var taskPrime = new Slides.TaskPrimeCalcer();taskPrime.GetPrimeCountClick += ButtonClick_Event;
using (new OperationTimer("Await tasks async")){await Task.WhenAll(taskPrime.Click(1, 10000000),
taskPrime.Click(10000000, 10000000),taskPrime.Click(20000000, 10000000));
}
async/await
15.04.2015 Толстиков Никита 21Многопоточность
• Ключевое слово await должно идти в паре с async
• await – это оператор, который манипулирует awaitable объектами
• Task – awaitable объект и для облегчения работы с ними введены методы расширения, возвращающие Task:– Task.Delay – аналог Thread.Sleep
– Task.WhenAny – аналог Task.WaitAny
– Task.WhenAll – аналог Task.WaitAll
– Task.Run или TaskFactory.StartNew вместо создания и запуска задачи
async/await
15.04.2015 Толстиков Никита 22Многопоточность
• Оператор async превращает метод в
statemachine:
async/await
15.04.2015 Толстиков Никита 23Многопоточность
• Оператор async превращает метод в
statemachine:
Применение
• TAP паттерн является наиболее
приемлемым и удобным решением для
асинхронного программирования на C#
поддерживаемым на уровне компилятора
• При помощи TAP паттерна можно
реализовывать и I/O операции и
вычисления
• При написании библиотек TAP паттерн
лучше использовать для I/O операций, а
большие вычисления проводить синхронно
15.04.2015 Толстиков Никита 24Многопоточность
Применение при вычислениях
• Генерировать вычислительные можно при помощи:
– Task.Factory.StartNew(…) (Task.Run c 4.5)
– Создать Task и вызвать Start (когда создание отделенно от запуска)
– ContinueWith – для генерации новой задачи, которая запустится после выполнения
– Статические у TaskFactory ContinueWhenAll и ContinueWhenAny
• Для long run задач, лучше всегда передавать CancelationToken
15.04.2015 Толстиков Никита 25Многопоточность
CancelationToken
• Task завершится в состоянии Canceled, если:– Запрос на завершение придет раньше чем задача
перейдет в состояние Running
– OperationCanceledException не будет обработана телом задачи
15.04.2015 Толстиков Никита 26Многопоточность
public Task<Bitmap> RenderAsync(ImageData data, CancellationToken cancellationToken)
{return Task.Run(() =>{
var bmp = new Bitmap(data.Width, data.Height);for(int y=0; y<data.Height; y++){
cancellationToken.ThrowIfCancellationRequested();for(int x=0; x<data.Width; x++){
… // render pixel [x,y] into bmp}
}return bmp;
}, cancellationToken);}
Применение при I/O
• Для совершения I/O опреций и задач, не требуищих для завершения основной поток лучше использовать TaskCompletionSource<TResult>
15.04.2015 Толстиков Никита 27Многопоточность
public static Task<Socket> AcceptAsync(this Socket socket) {
if (socket == null) throw new ArgumentNullException("socket"); var tcs = new TaskCompletionSource<Socket>(); socket.BeginAccept(asyncResult => {
try{
var s = asyncResult.AsyncState as Socket;var client = s.EndAccept(asyncResult);tcs.SetResult(client);
} catch (Exception ex) {
tcs.SetException(ex);}
}, socket); return tcs.Task;
}
var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); listener.Bind(new IPEndPoint(IPAddress.Loopback, 2610)); listener.Listen(10); var client = await listener.AcceptAsync();
Concurrent collections
• Потоко-безопасные коллекции находятся в System.Collections.Concurrent
• Потоко-безопасные коллекции во всем уступают обычным коллекциям, кроме потоко-безопасности
• Использование этих коллекций не делает ваш код потоко-безопасным
• Модификация во время итерации не выбрасывает исключение
15.04.2015 Толстиков Никита 28Многопоточность
IProducerConsumerCollection<T>
• Производитель/потребитель коллекции выполняют два действия:
–Добавить элемент в коллекцию (produce)
–Получить элемент из коллекции, удалив его (consume)
• Классический пример Stack и Queue
• Частый прецедент при многопочномисполнении
15.04.2015 Толстиков Никита 29Многопоточность
IProducerConsumerCollection<T>
• Наследники коллекции:
–ConcurrentStack<T>–ConcurrentQueue<T>–ConcurrentBag<T>
15.04.2015 Толстиков Никита 30Многопоточность
public interface IProducerConsumerCollection<T> : IEnumerable<T>, ICollection, IEnumerable
{bool TryAdd(T item); //Attempts to add an object to the
//collectionbool TryTake(out T item); //Attempts to remove and return an
//object from the collectionT[] ToArray(); //Copies the elements contained in the to a new
//array}
ConcurrentBag<T>
• Потоко-безопасная коллекция
неупорядочынх объектов с дубликатами
• Предоставляет отдельный список для
каждого потока
• Используется при многопоточной
обработке коллекций
15.04.2015 Толстиков Никита 31Многопоточность
public class ConcurrentBag<T> : IProducerConsumerCollection<T>,IEnumerable<T>, ICollection, IEnumerable
{...
}
BlockingCollection<T>
• Обертка над IProducerConsumerCollection,
• Добавляет метод Take, который блокирует коллекцию пока в ней не появятся элементы
• В конструкторе принимает коллекцию
• По умолчанию Queue
• Можно взять GetConsumingEnumerable, который будет итерироваться по элементам в процессе их появления
15.04.2015 Толстиков Никита 32Многопоточность
BlockingCollection<T>
15.04.2015 Толстиков Никита 33Многопоточность
public class PCQueue : IDisposable{
BlockingCollection<Action> _taskQ = new BlockingCollection<Action>();public PCQueue(int workerCount){
// Create and start a separate Task for each consumer:for (int i = 0; i < workerCount; i++)
Task.Factory.StartNew(Consume);}
public void Dispose() { _taskQ.CompleteAdding(); }
public void EnqueueTask(Action action) { _taskQ.Add(action); }
void Consume(){
// This sequence that we’re enumerating will block when no elements// are available and will end when CompleteAdding is called. foreach (Action action in _taskQ.GetConsumingEnumerable())
action(); // Perform task.}
}
The End
15.04.2015 Толстиков Никита 34LINQ