620 likes | 770 Views
Krótka historia asynchroniczności w .NET 1-4.5. Jakub Binkowski. O mnie. Jakub Binkowski Senior .NET Developer @ Rule Financial Microsoft MVP w latach 2008-2011 MCP, MCTS, MCPD Lider Łódzkiej Grupy Profesjonalistów IT & .NET Jakub@Binkowski.com.pl. Agenda. Dlaczego asynchroniczność?
E N D
Krótka historiaasynchroniczności w .NET 1-4.5 Jakub Binkowski
O mnie • Jakub Binkowski • Senior .NET Developer @ Rule Financial • Microsoft MVP w latach 2008-2011 • MCP, MCTS, MCPD • Lider Łódzkiej Grupy Profesjonalistów IT & .NET • Jakub@Binkowski.com.pl
Agenda • Dlaczego asynchroniczność? • Jak? • Asynchronous Programming Model (APM) • Event-basedAsynchronousPattern (EAP) • Task-basedAsynchronousPattern (TAP) • Async i await w .NET 4.5 • Asynchroniczny ASP.NET • Testy jednostkowe
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 publicActionResultAvatarDetails(){varurl = "http://...";var data = newWebClient().DownloadData(url); var bitmap = newBitmap(newMemoryStream(data));var model = newAvatarModel {AvatarUrl = url,Size = data.Length,Width = bitmap.Width,Height = bitmap.Height };returnView(model);}
Gdzie jest problem? • Pula wątków obsługujących żądania • Dla uproszczenia – pula = 3 wątki; 3 procesory HTML Web Service HTML Web Service DB HTML CPU = 66%
Gdzie jest problem? • Pula wątków obsługujących żądania • Dla uproszczenia – pula = 3 wątki; 3 procesory DB Web Service HTML HTML HTML Web Service CPU = 33%
Gdzie jest problem? • Pula wątków obsługujących żądania • Dla uproszczenia – pula = 3 wątki; 3 procesory Web Service HTML HTML HTML Web Service DB ! CPU = 0%
Operacje obliczeniowe Wątek
Operacje I/O (synchroniczne) Wątek Urządzenie Operacja I/O
Gdyby… • 1 cykl procesora = 1 sekunda: • Rejestr: 1s • Pamięć: 5-10s • Dysk: 3 miesiące • Sieć: 30 lat
Przykład 2 - WPF voidButton_Click(objectsender, RoutedEventArgs e){var data = newWebClient().DownloadData(tbUrl.Text);var bitmap = newBitmapImage();bitmap.BeginInit();bitmap.StreamSource = newMemoryStream(data);bitmap.EndInit();imgAvatar.Source = bitmap;} Uwaga! Brzydki kod
Przykład 2 – WPF (aplikacja desktop) • Tylko jeden wątek obsługuje UI • W czasie wykonywania operacji aplikacja nie odpowiada
Programowanie synchroniczneProblemy • 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: publicinterfaceIMyType { intDoSomething(int a); } • Wywołanie: IMyTypetype = /*..*/; intresult = type.DoSomething(10);
Asynchronous Programming ModelAPI publicinterfaceIMyType { IAsyncResultBeginDoSomething(inta, AsyncCallbackcallback, objectstate); intEndDoSomething(IAsyncResultasyncResult); }
Asynchronous Programming Model Użycie – wariant 1 IMyTypetype = /*..*/; IAsyncResultasyncRes= type.BeginDoSomething(10, null, null); //... intresult = type.EndDoSomething(asyncRes); //...
Asynchronous Programming ModelUżycie – wariant 2 classMyProgram { privateIMyType _type; publicvoid Begin() { _type.BeginDoSomething(10, OnDoSomethingFinished, null); } privatevoidOnDoSomethingFinished(IAsyncResultasyncRes) { intresult = _type.EndDoSomething(asyncRes); //... } }
Asynchronous Programming ModelUżycie – wariant 2b IMyTypetype = /*..*/; type.BeginDoSomething(10, asyncRes => { intresult = type.EndDoSomething(asyncRes); //... }, null);
Trochę historii • Oryginalnie IAsyncResult jedynym wzorcem • Wg zespołu Windows FormsIAsyncResult jest • zbyt skomplikowany • nie pasuje do trybu design • wymaga pilnowania wątków • Efekt – alternatywne podejście:Event-baseAsynchronous Programming
Event-basedAsynchronous ProgrammingAPI publicinterfaceIMyType { voidDoSomethingAsync(int a); eventDoSomethingCompletedEventHandlerDoSomethingCompleted; } publicclassDoSomethingCompletedEventArgs: AsyncCompletedEventArgs { publicint Result { get; set; }/*…*/ } publicdelegatevoidDoSomethingCompletedEventHandler(objectsender, DoSomethingCompletedEventArgsargs);
Event-basedAsynchronous ProgrammingUżycie IMyTypetype = /*...*/; type.DoSomethingCompleted += (sender, args) => { intresult = args.Result; //... }; type.DoSomethingAsync(10);
APMZastosowanie • 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
EAPZastosowanie • FCL: 17 klas • System.ComponentModel.BackgroundWorker; • System.Net.WebClient; • System.Net.NetworkInformation.Ping; • System.Windows.Forms.PictureBox; • System.Net.Mail.SmtpClient; • i inne…
W drodze ku lepszemu • .NET 4.0 wprowadził nowy model programowania równoległego – TaskParallel Library • C# 4.5 wprowadzi nowy model programowania asynchronicznego:Task-basedAsynchronous Programming • APM i EAP staną się przestarzałe
Idea TAP • Niech deweloperzy piszą kod prawie tak samo • „Magią” niech zajmie się kompilator
Task-basedAsynchronous ProgrammingAPI publicinterfaceIMyType { Task<int> DoSomethingTask(inta); }
Task-basedAsynchronous ProgrammingAPI publicinterfaceIMyType { Task<int> DoSomethingTask(inta); }
Task-basedAsynchronous ProgrammingUżycie publicasyncTask<bool> IsSomethingZeroAsync(int a) { IMyTypetype = /*...*/; varvalue = awaittype.DoSomethingAsync(a); returnvalue == 0; }
Task-basedAsynchronous ProgrammingUżycie publicasyncTask<bool> IsSomethingZeroAsync(int a) { IMyTypetype = /*...*/; varvalue = awaittype.DoSomethingAsync(a); returnvalue == 0; }
Task-basedAsynchronous ProgrammingUżycie publicasyncTask<bool> IsSomethingZeroAsync(int a) { IMyTypetype = /*...*/; varvalue = awaittype.DoSomethingAsync(a); returnvalue == 0; }
Task-basedAsynchronous ProgrammingUżycie publicasyncTask<bool> IsSomethingZeroAsync(int a) { IMyTypetype = /*...*/; varvalue = awaittype.DoSomethingAsync(a); returnvalue == 0; }
ASP.NET Asynchroniczne ASP.NET MVC 3 i 4
Synchroniczny kontrolerASP.NET MVC 3 publicclassHomeController : Controller{ publicActionResultAvatarDetails(){varclient = newWebClient(); vardata = client.DownloadData(newUri("…"));//...return View();}}
Asynchroniczny kontrolerASP.NET MVC 3 publicclassHomeController : AsyncController{ publicvoidAvatarDetailsAsync(){AsyncManager.OutstandingOperations.Increment();var client = newWebClient();client.DownloadDataCompleted += (s, a) => {AsyncManager.Parameters["data"] = a.Result;AsyncManager.OutstandingOperations.Decrement(); };client.DownloadDataAsync(newUri("…")); }publicActionResultAvatarDetailsCompleted(byte[] data) {//...return View(); }}
Asynchroniczny kontrolerASP.NET MVC 3 publicclassHomeController : AsyncController{ publicvoidAvatarDetailsAsync() {AsyncManager.OutstandingOperations.Increment();var client = newWebClient();client.DownloadDataCompleted += (s, a) => {AsyncManager.Parameters["data"] = a.Result;AsyncManager.OutstandingOperations.Decrement(); };client.DownloadDataAsync(newUri("…")); }publicActionResultAvatarDetailsCompleted(byte[] data) {//...return View(); }}
Asynchroniczny kontrolerASP.NET MVC 3 publicclassHomeController : AsyncController{ publicvoidAvatarDetailsAsync() {AsyncManager.OutstandingOperations.Increment();var client = newWebClient();client.DownloadDataCompleted += (s, a) => {AsyncManager.Parameters["data"] = a.Result;AsyncManager.OutstandingOperations.Decrement(); };client.DownloadDataAsync(newUri("…")); }publicActionResultAvatarDetailsCompleted(byte[] data) {//...return View(); }}
Asynchroniczny kontrolerASP.NET MVC 3 publicclassHomeController : AsyncController{ publicvoidAvatarDetailsAsync() {AsyncManager.OutstandingOperations.Increment();var client = newWebClient();client.DownloadDataCompleted += (s, a) => {AsyncManager.Parameters["data"] = a.Result;AsyncManager.OutstandingOperations.Decrement(); };client.DownloadDataAsync(newUri("…")); }publicActionResultAvatarDetailsCompleted(byte[] data) {//...return View(); }}