500 likes | 671 Views
Architektura aplikacji wielowątkowych. Jakub Binkowski. Jakub Binkowski. 2008-2011. Lead .NET Developer. jakub@binkowski.com.pl. Cel prezentacji. Alternatywne podejście przy projektowaniu aplikacji wielowątkowych Częste błędy. Przykładowe rozwiązanie – silnik giełdy. Jak działa giełda?.
E N D
Architektura aplikacji wielowątkowych Jakub Binkowski
Jakub Binkowski 2008-2011 Lead .NET Developer jakub@binkowski.com.pl
Cel prezentacji • Alternatywne podejście przy projektowaniu aplikacji wielowątkowych • Częste błędy
Jak działa giełda? Produkty Sprzedający Kupujący
Jak działa rynek? Oferty zakupu Oferty sprzedaży
Jak działa rynek? Oferty zakupu Oferty sprzedaży
Jak działa rynek? Oferty zakupu Oferty sprzedaży
Jak działa rynek? Oferty zakupu Oferty sprzedaży
Jak działa rynek? Oferty zakupu Oferty sprzedaży
Jak działa rynek? Oferty zakupu Oferty sprzedaży Transakcja Anna sprzedaje Janowi 30 szt. w cenie 100,00 zł/szt.
Jak działa rynek? Oferty zakupu Oferty sprzedaży
Jak działa rynek? Oferty zakupu Oferty sprzedaży
Jak działa rynek? Oferty zakupu Oferty sprzedaży Transakcja Zenon sprzedaje Janowi 20 szt. w cenie 100,00 zł/szt.
Jak działa rynek? Oferty zakupu Oferty sprzedaży
Jak działa rynek? Oferty zakupu Oferty sprzedaży Transakcja Zenon sprzedaje Ewie 100 szt. w cenie 95,00 zł/szt.
Jak działa rynek? Oferty zakupu Oferty sprzedaży
Architektura z lotu ptaka Oferty sprzedaży Silnik rynku Transakcje Oferty zakupu Aktualizacje ofert
Przetworzenie oferty • Odebranie wiadomości • Audyt (zapis na dysk) • Deserializacja (byte[] object) • Umieszczenie na rynku Oferty sprzedaży Silnik rynku Oferty zakupu
Przetworzenie transakcji • Wygenerowanie przez rynek • Serializacja (object byte[]) • Audyt (zapisanie na dysk) • Wysłanie do strony kupującej i sprzedającej Silnik rynku Transakcje
Przetworzenie aktualizacji oferty • Wygenerowanie przez rynek • Serializacja (object byte[]) • Audyt (zapisanie na dysk) • Wysłanie do składającego ofertę Silnik rynku Aktualizacje ofert
Wymagania • Niezawodność / bezbłędność • Wysoka testowalność rozwiązania • Pełny audyt • Możliwość odtworzenia dowolnej sytuacji • Wysoka wydajność • Optymalizacja kodu
Architektura „standardowa” • Przychodząca wiadomość = 1 wątek • Wątki brane z puli (ThreadPool) Pula wątków w .NET: • Zbiór wątków dostępnych dla aplikacji • Minimalna ilość wątków = ilość procesorów logicznych • Maksymalna – zależy od środowiska
Obsługa wiadomości przychodzących Dedykowany wątek Pula wątków Wiadomości przychodzące
W czym jest problem? lock Serializacja Audyt
W czym jest problem? Tylko tutaj potrzebny jest lock! lock
„lock leak” lock (sync){//... _eventConsumer.ProcessOfferUpdated(/*...*/); //...} lock (sync){//...var handler = OfferUpdated;if (handler != null) { handler(this, EventArgs.Empty); }//...} Przekazanie kontroli do zewnętrznego kodu
„lock leak” - konsekwencje • Utrata wydajności • Możliwe deadlocki • Możliwe zapchanie się puli wątków OutOfMemoryException
„lock leak” - jak uniknąć? boolofferUpdated = false;lock (sync){//...offerUpdated = true;//...}if (offerUpdated) _eventConsumer.ProcessOfferUpdated(/*..*/); • Uwaga! Wywoływanie powiadomień za sekcją krytyczną może wpłynąć na logikę działania aplikacji.
Czy spełniliśmy wymagania? • Niezawodność / bezbłędność • Wysoka testowalność rozwiązania • Czy nasz kod można dobrze przetestować? • Pełny audyt • Możliwość odtworzenia dowolnej sytuacji • Czy wykonanie nie jest losowe? • Wysoka wydajność • Optymalizacja kodu • Czy nie da się szybciej?
Architektura oparta o kolejki Wysokowydajne kolejki wielowątkowe
Architektura oparta o kolejki Wątki wykonujące kolejne operacje
Architektura oparta o kolejki Wiadomości przychodzące Audyt Deserializacja Przetwarzanie przez rynek Wiadomości wychodzące Serializacja Audyt
Jak to zrealizować (w .NET 4)? • ConcurrentQueue<T> • wielowątkowo bezpieczna kolejka • wysoko zoptymalizowana • BlockingCollection<T> • opakowanie na kolekcje • wspiera scenariusz producent-konsument • pozwala na blokowanie odczytu (kolekcja pusta) • pozwala na blokowanie zapisu (kolekcja pełna)
BlockingCollection<T> - przykład • Deklaracja:var queue = newBlockingCollection<int>(100); Maksymalna pojemność kolekcji
BlockingCollection<T> - przykład • Producent:queue.Add(1);queue.Add(2);queue.Add(3);queue.CompleteAdding(); Wstawianie elementów Zakończenie dodawania
BlockingCollection<T> - przykład • Konsument:var consumer = queue.GetConsumingEnumerable();foreach (var number in consumer){Console.WriteLine(number);} Oczekiwanie na elementy (przy pustej kolekcji) Enumeracja jak przy zwykłej kolekcji
Architektura oparta o kolejkiZalety • Eliminacja wielowątkowości • Wiele wątków, ale kolejki zajmują się synchronizacją • Kod łatwy do testowania • Przewidywalność zachowania • Powtórzenie danych wejściowych zawsze da taki sam rezultat • Wyższa wydajność • Mniej wątków = mniejsza konieczność synchronizacji • Możliwość grupowego przetwarzania
Architektura oparta o kolejkiZalety • Bardziej przejrzysty design • Jasno określone wątki aplikacji • Dobrze zdefiniowane punkty interakcji pomiędzy wątkami (kolejki) • Pełny wgląd w aktualny stan aplikacji • Ilość elementów w kolejce • Łatwa lokalizacja wąskich gardeł
Architektura oparta o kolejkiWady • Trudniejsze debugowanie • Bardziej pracochłonna • Wymaga dokumentacji
Gdzie taka architektura się sprawdzi? Aplikacje: • przetwarzające niewiele rodzajów zdarzeń • o relatywnie prostej logice • mocno obciążone (wymagana wysoka wydajność) Na przykład: • systemy giełdowe • systemy agregujące/przetwarzające dane • aplikacje dostarczające dane „na żywo”
Podsumowanie • Klasyczne podejście „operacja = wątek” nie zawsze najwydajniejsze • Uwaga na wywołania zewnętrzne wewnątrz lock! • Kolejki mogą rozwiązać problem synchronizacji pomiędzy wątkami