34
Толстиков Никита [email protected] Многопоточное программирование 15.04.2015 1 Многопоточность

Net Framework и С#, весна 2015: Многопоточное программирование

Embed Size (px)

Citation preview

Page 1: Net Framework и С#, весна 2015: Многопоточное программирование

Толстиков Никита

[email protected]

Многопоточное программирование

15.04.2015 1Многопоточность

Page 2: Net Framework и С#, весна 2015: Многопоточное программирование

План лекции

• APM

• EAP

• TAP

• Concurrent collections

15.04.2015 Толстиков Никита 2Многопоточность

Page 3: Net Framework и С#, весна 2015: Многопоточное программирование

Асинхронное программирование

• Основные паттерны:

– Asynchronous Programming Model (APM)

– Event-based Asynchronous Pattern (EAP)

– Task-based Asynchronous Pattern (TAP)

15.04.2015 Толстиков Никита 3Многопоточность

Page 4: Net Framework и С#, весна 2015: Многопоточное программирование

Asynchronous Programming Model

• Использует асинхронные делегаты и

IAsyncResult объекты

• Основная идея использовать два метода:

– BeginOperationName – для старта

асинхронной операции

– EndOperationName – для получения

результатов

• Использовалась в legacy системах до 4.0 .Net

• Depricated

15.04.2015 Толстиков Никита 4Многопоточность

Page 5: Net Framework и С#, весна 2015: Многопоточное программирование

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));

}}

Page 6: Net Framework и С#, весна 2015: Многопоточное программирование

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));

}

Page 7: Net Framework и С#, весна 2015: Многопоточное программирование

Event-based Asynchronous Pattern

• Использует асинхронные события и их

обработчики

• Основная идея использовать 3 типа методов в

классе:

– MethodNameAsync– для старта асинхронной

операции

– MethodNameCompletedEvent – событие

завершения операции

– MethodNameCancel– для остановки операции

• Depricated

15.04.2015 Толстиков Никита 7Многопоточность

Page 8: Net Framework и С#, весна 2015: Многопоточное программирование

События

• Это механизм класса сообщать его

пользователю, что слчуилось что то

интересное

• Пользователь сам может решать что ему

интерсно – подписываться на события

• Такая модель взаимодействия

называется publisher-subscriber

15.04.2015 Толстиков Никита 8Многопоточность

Page 9: Net Framework и С#, весна 2015: Многопоточное программирование

События

• В С# события – это специальные

объекты, которые при сробатываении

запускает все подписанные на себя

делигаты

• Cобытия вводятся при помощи ключевого

слова event

15.04.2015 Толстиков Никита 9Многопоточность

Page 10: Net Framework и С#, весна 2015: Многопоточное программирование

Пример

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");}

Page 11: Net Framework и С#, весна 2015: Многопоточное программирование

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.}

Page 12: Net Framework и С#, весна 2015: Многопоточное программирование

Event-based Asynchronous Pattern

• Перегрузка методов с использованием

userState позволяет вызывать Async операции

не дожидаясь выполнения предыдущих

• CancelAsync – позволяет остановить

запущенную операцию по userStata

• WebClient и BackgroundWorker представляют

собой наиболее распространненые примеры

EAP

15.04.2015 Толстиков Никита 12Многопоточность

Page 13: Net Framework и С#, весна 2015: Многопоточное программирование

Реализация

здесь

15.04.2015 Толстиков Никита 13Многопоточность

Page 14: Net Framework и С#, весна 2015: Многопоточное программирование

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;

}

Page 15: Net Framework и С#, весна 2015: Многопоточное программирование

Реализация

• Ручная– Метод, который возвращает Task или

Task<TResult>

– Программист сам обрабатывает исключения

• Компилятор– Метод, который возвращает значение, но помечен

словом async

– Компилятор сам генерирует код обработки ошибок и

оборачивания в Task

• Гибридная

15.04.2015 Толстиков Никита 15Многопоточность

Page 16: Net Framework и С#, весна 2015: Многопоточное программирование

Ручная

• Все задачи создаются в ручную: Task.Run()

• Завершение задач ожидается в ручную

• Исключения выбрасываются в месте, где

вызывается свойство Result, метода Wait() или

Task.WaitAll()/Task.WaitAny()

• Все исключения из Task обернуты в

AggregationException

15.04.2015 Толстиков Никита 16Многопоточность

Page 17: Net Framework и С#, весна 2015: Многопоточное программирование

Ручная

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));}

}

Page 18: Net Framework и С#, весна 2015: Многопоточное программирование

Ручная

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));

}

Page 19: Net Framework и С#, весна 2015: Многопоточное программирование

Компилятор

• Все задачи создаются в ручную : Task.Run()

• Вводится при помощи ключевых слов async/await

• await – асинхронное ожидание, не

блокирует поток, а ставит его на паузу

• Исключение выбрасывается в await

• Все исключения перебрасываются в

первозданном виде15.04.2015 Толстиков Никита 19Многопоточность

Page 20: Net Framework и С#, весна 2015: Многопоточное программирование

Компилятор

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));

}

Page 21: Net Framework и С#, весна 2015: Многопоточное программирование

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 вместо создания и запуска задачи

Page 22: Net Framework и С#, весна 2015: Многопоточное программирование

async/await

15.04.2015 Толстиков Никита 22Многопоточность

• Оператор async превращает метод в

statemachine:

Page 23: Net Framework и С#, весна 2015: Многопоточное программирование

async/await

15.04.2015 Толстиков Никита 23Многопоточность

• Оператор async превращает метод в

statemachine:

Page 24: Net Framework и С#, весна 2015: Многопоточное программирование

Применение

• TAP паттерн является наиболее

приемлемым и удобным решением для

асинхронного программирования на C#

поддерживаемым на уровне компилятора

• При помощи TAP паттерна можно

реализовывать и I/O операции и

вычисления

• При написании библиотек TAP паттерн

лучше использовать для I/O операций, а

большие вычисления проводить синхронно

15.04.2015 Толстиков Никита 24Многопоточность

Page 25: Net Framework и С#, весна 2015: Многопоточное программирование

Применение при вычислениях

• Генерировать вычислительные можно при помощи:

– Task.Factory.StartNew(…) (Task.Run c 4.5)

– Создать Task и вызвать Start (когда создание отделенно от запуска)

– ContinueWith – для генерации новой задачи, которая запустится после выполнения

– Статические у TaskFactory ContinueWhenAll и ContinueWhenAny

• Для long run задач, лучше всегда передавать CancelationToken

15.04.2015 Толстиков Никита 25Многопоточность

Page 26: Net Framework и С#, весна 2015: Многопоточное программирование

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);}

Page 27: Net Framework и С#, весна 2015: Многопоточное программирование

Применение при 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();

Page 28: Net Framework и С#, весна 2015: Многопоточное программирование

Concurrent collections

• Потоко-безопасные коллекции находятся в System.Collections.Concurrent

• Потоко-безопасные коллекции во всем уступают обычным коллекциям, кроме потоко-безопасности

• Использование этих коллекций не делает ваш код потоко-безопасным

• Модификация во время итерации не выбрасывает исключение

15.04.2015 Толстиков Никита 28Многопоточность

Page 29: Net Framework и С#, весна 2015: Многопоточное программирование

IProducerConsumerCollection<T>

• Производитель/потребитель коллекции выполняют два действия:

–Добавить элемент в коллекцию (produce)

–Получить элемент из коллекции, удалив его (consume)

• Классический пример Stack и Queue

• Частый прецедент при многопочномисполнении

15.04.2015 Толстиков Никита 29Многопоточность

Page 30: Net Framework и С#, весна 2015: Многопоточное программирование

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}

Page 31: Net Framework и С#, весна 2015: Многопоточное программирование

ConcurrentBag<T>

• Потоко-безопасная коллекция

неупорядочынх объектов с дубликатами

• Предоставляет отдельный список для

каждого потока

• Используется при многопоточной

обработке коллекций

15.04.2015 Толстиков Никита 31Многопоточность

public class ConcurrentBag<T> : IProducerConsumerCollection<T>,IEnumerable<T>, ICollection, IEnumerable

{...

}

Page 32: Net Framework и С#, весна 2015: Многопоточное программирование

BlockingCollection<T>

• Обертка над IProducerConsumerCollection,

• Добавляет метод Take, который блокирует коллекцию пока в ней не появятся элементы

• В конструкторе принимает коллекцию

• По умолчанию Queue

• Можно взять GetConsumingEnumerable, который будет итерироваться по элементам в процессе их появления

15.04.2015 Толстиков Никита 32Многопоточность

Page 33: Net Framework и С#, весна 2015: Многопоточное программирование

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.}

}

Page 34: Net Framework и С#, весна 2015: Многопоточное программирование

The End

15.04.2015 Толстиков Никита 34LINQ