Krótka historia asynchroniczności w.NET Jakub Binkowski
O mnie Jakub Binkowski Senior.NET Rule Financial Microsoft MVP w latach MCP, MCTS, MCPD Lider Łódzkiej Grupy Profesjonalistów IT &.NET
Agenda Dlaczego asynchroniczność? Jak? Asynchronous Programming Model (APM) Event-based Asynchronous Pattern (EAP) Task-based Asynchronous Pattern (TAP) Async i await w.NET 4.5 Asynchroniczny ASP.NET Testy jednostkowe
Dlaczego asynchroniczność?
Przypomnienie: Wątek jest drogi Pamięć: ponad 1MB (pamięć zarezerwowana) Czas: Powiadomienia o utworzeniu i zamknięciu wątku (do wszystkich DLL) Przełączenia kontekstu
Przypomnienie: Pula wątków Zbiór wątków dostępnych dla aplikacji Jedna na proces (CLR) Ilość wątków w puli Minimalna ilość wątków = ilość procesorów logicznych Maksymalna – zależy od środowiska Algorytm – czarna skrzynka Klasa ThreadPool
Przykład 1 – ASP.NET
public ActionResult AvatarDetails() { var url = " var data = new WebClient().DownloadData(url); var bitmap = new Bitmap(new MemoryStream(data)); var model = new AvatarModel { AvatarUrl = url, Size = data.Length, Width = bitmap.Width, Height = bitmap.Height }; return View(model); }
Gdzie jest problem? Pula wątków obsługujących żądania Dla uproszczenia – pula = 3 wątki; 3 procesory HTML DB Web Service HTML CPU = 66% Web Service
Gdzie jest problem? Pula wątków obsługujących żądania Dla uproszczenia – pula = 3 wątki; 3 procesory HTML DB Web Service CPU = 33% Web Service HTML
Gdzie jest problem? Pula wątków obsługujących żądania Dla uproszczenia – pula = 3 wątki; 3 procesory HTML DB Web Service CPU = 0% Web Service HTML !
Operacje obliczeniowe Wątek
Operacje I/O (synchroniczne) Operacja I/O Wątek Urządzenie
Gdyby… 1 cykl procesora = 1 sekunda: Rejestr: 1s Pamięć: 5-10s Dysk: 3 miesiące Sieć: 30 lat
Przykład 2 - WPF
void Button_Click(object sender, RoutedEventArgs e) { var data = new WebClient().DownloadData(tbUrl.Text); var bitmap = new BitmapImage(); bitmap.BeginInit(); bitmap.StreamSource = new MemoryStream(data); bitmap.EndInit(); imgAvatar.Source = bitmap; } Uwaga! Brzydki kod Uwaga! Brzydki kod
Przykład 2 – WPF (aplikacja desktop) Tylko jeden wątek obsługuje UI W czasie wykonywania operacji aplikacja nie odpowiada
Programowanie synchroniczne Problemy Problem 1: Pula wątków blokowana przez zewnętrzne urządzenia (I/O) Problem 2: Wątek UI zajęty przez długotrwałą operację
Wzorce asynchroniczne APM vs EAP vs TAP
Kod synchroniczny API: public interface IMyType { int DoSomething(int a); } Wywołanie: IMyType type = /*..*/; int result = type.DoSomething(10);
Asynchronous Programming Model API public interface IMyType { IAsyncResult BeginDoSomething(int a, AsyncCallback callback, object state); int EndDoSomething(IAsyncResult asyncResult); }
Asynchronous Programming Model Użycie – wariant 1 IMyType type = /*..*/; IAsyncResult asyncRes = type.BeginDoSomething(10, null, null); //... int result = type.EndDoSomething(asyncRes); //...
Asynchronous Programming Model Użycie – wariant 2 class MyProgram { private IMyType _type; public void Begin() { _type.BeginDoSomething(10, OnDoSomethingFinished, null); } private void OnDoSomethingFinished(IAsyncResult asyncRes) { int result = _type.EndDoSomething(asyncRes); //... }
Asynchronous Programming Model Użycie – wariant 2b IMyType type = /*..*/; type.BeginDoSomething(10, asyncRes => { int result = type.EndDoSomething(asyncRes); //... }, null);
Demo Asynchronous Programming Model
Trochę historii Oryginalnie IAsyncResult jedynym wzorcem Wg zespołu Windows Forms IAsyncResult jest zbyt skomplikowany nie pasuje do trybu design wymaga pilnowania wątków Efekt – alternatywne podejście: Event-base Asynchronous Programming
Event-based Asynchronous Programming API public interface IMyType { void DoSomethingAsync(int a); event DoSomethingCompletedEventHandler DoSomethingCompleted; } public class DoSomethingCompletedEventArgs: AsyncCompletedEventArgs { public int Result { get; set; } /*…*/ } public delegate void DoSomethingCompletedEventHandler( object sender, DoSomethingCompletedEventArgs args);
Event-based Asynchronous Programming Użycie IMyType type = /*...*/; type.DoSomethingCompleted += (sender, args) => { int result = args.Result; //... }; type.DoSomethingAsync(10);
Demo Event-based Asynchronous Programming
APM Zastosowanie FCL: 60 klas Strumienie ( System.IO.Stream ) DNS ( System.Net.Dns ) Gniazda sieciowe ( System.Net.Sockets.Socket ) Żądania sieciowe ( System.Net.WebRequest ) Polecenia SQL ( System.Data.SqlClient.SqlCommand ) Porty ( System.IO.Ports.SerialPort ) Web/Service References
EAP Zastosowanie FCL: 17 klas System.ComponentModel.BackgroundWorker; System.Net.WebClient; System.Net.NetworkInformation.Ping; System.Windows.Forms.PictureBox; System.Net.Mail.SmtpClient; i inne…
APM czy EAP?
Gdzie jest problem?
Gdzie jest problem?
W drodze ku lepszemu.NET 4.0 wprowadził nowy model programowania równoległego – Task Parallel Library C# 4.5 wprowadzi nowy model programowania asynchronicznego: Task-based Asynchronous Programming APM i EAP staną się przestarzałe
Idea TAP Niech deweloperzy piszą kod prawie tak samo Magią niech zajmie się kompilator
Task-based Asynchronous Programming API public interface IMyType { Task DoSomethingTask(int a); }
Task-based Asynchronous Programming API public interface IMyType { Task DoSomethingTask(int a); }
Task-based Asynchronous Programming Użycie public async Task IsSomethingZeroAsync(int a) { IMyType type = /*...*/; var value = await type.DoSomethingAsync(a); return value == 0; }
Task-based Asynchronous Programming Użycie public async Task IsSomethingZeroAsync(int a) { IMyType type = /*...*/; var value = await type.DoSomethingAsync(a); return value == 0; }
Task-based Asynchronous Programming Użycie public async Task IsSomethingZeroAsync(int a) { IMyType type = /*...*/; var value = await type.DoSomethingAsync(a); return value == 0; }
Task-based Asynchronous Programming Użycie public async Task IsSomethingZeroAsync(int a) { IMyType type = /*...*/; var value = await type.DoSomethingAsync(a); return value == 0; }
Demo Task-based Asynchronous Programming
ASP.NET Asynchroniczne ASP.NET MVC 3 i 4
Demo ASP.NET MVC 3
Synchroniczny kontroler ASP.NET MVC 3 public class HomeController : Controller { public ActionResult AvatarDetails() { var client = new WebClient(); var data = client.DownloadData(new Uri("…")); //... return View(); } }
Asynchroniczny kontroler ASP.NET MVC 3 public class HomeController : AsyncController { public void AvatarDetailsAsync() { AsyncManager.OutstandingOperations.Increment(); var client = new WebClient(); client.DownloadDataCompleted += (s, a) => { AsyncManager.Parameters["data"] = a.Result; AsyncManager.OutstandingOperations.Decrement(); }; client.DownloadDataAsync(new Uri("…")); } public ActionResult AvatarDetailsCompleted(byte[] data) { //... return View(); } }
Asynchroniczny kontroler ASP.NET MVC 3 public class HomeController : AsyncController { public void AvatarDetailsAsync() { AsyncManager.OutstandingOperations.Increment(); var client = new WebClient(); client.DownloadDataCompleted += (s, a) => { AsyncManager.Parameters["data"] = a.Result; AsyncManager.OutstandingOperations.Decrement(); }; client.DownloadDataAsync(new Uri("…")); } public ActionResult AvatarDetailsCompleted(byte[] data) { //... return View(); } }
Asynchroniczny kontroler ASP.NET MVC 3 public class HomeController : AsyncController { public void AvatarDetailsAsync() { AsyncManager.OutstandingOperations.Increment(); var client = new WebClient(); client.DownloadDataCompleted += (s, a) => { AsyncManager.Parameters["data"] = a.Result; AsyncManager.OutstandingOperations.Decrement(); }; client.DownloadDataAsync(new Uri("…")); } public ActionResult AvatarDetailsCompleted(byte[] data) { //... return View(); } }
Asynchroniczny kontroler ASP.NET MVC 3 public class HomeController : AsyncController { public void AvatarDetailsAsync() { AsyncManager.OutstandingOperations.Increment(); var client = new WebClient(); client.DownloadDataCompleted += (s, a) => { AsyncManager.Parameters["data"] = a.Result; AsyncManager.OutstandingOperations.Decrement(); }; client.DownloadDataAsync(new Uri("…")); } public ActionResult AvatarDetailsCompleted(byte[] data) { //... return View(); } }
Demo ASP.NET MVC 4 + async
Synchroniczny kontroler ASP.NET MVC 4 public class HomeController : Controller { public ActionResult AvatarDetails() { var client = new WebClient(); var data = client.DownloadData(new Uri("…")); //... return View(); } }
Asynchroniczny kontroler ASP.NET MVC 4 public class HomeController : AsyncController { public async Task AvatarDetails() { var client = new WebClient(); var data = await client.DownloadDataTaskAsync(new Uri("…")); //... return View(); } }
Asynchroniczny kontroler ASP.NET MVC 4 public class HomeController : AsyncController { public async Task AvatarDetails() { var client = new WebClient(); var data = await client.DownloadDataTaskAsync(new Uri("…")); //... return View(); } }
Asynchroniczny kontroler ASP.NET MVC 4 public class HomeController : AsyncController { public async Task AvatarDetails() { var client = new WebClient(); var data = await client.DownloadDataTaskAsync(new Uri("…")); //... return View(); } }
async a testy jednostkowe
Demo Synchronous unit tests
Demo Asynchronous unit tests
Podsumowanie 3 wzorce programowania asynchronicznego: APM (Asynchronous Programming Model) EAP (Event-based Asynchronous Programming) TAP (Task-based Asynchronous Programming) TAP zastępuje APM i EAP nowe metody w całym.NET Framework wsparcie w ASP.NET wsparcie w WCF Potężne wsparcie kompilatora – async i await
Przydatne odnośniki Asynchronous programming design patterns: Asynchronous ASP.NET: Asynchronous WCF:
Przydatne odnośniki – async TAP in-depth: Visual Studio 11 CTP: aylang=en&id= aylang=en&id=27543 ASP.NET MVC 4 CTP: MVC4VS11&prerelease=true MVC4VS11&prerelease=true Jon Skeet blog:
Dziękuję za uwagę. Pytania?