Zwątpiłem w edukację

Po trzecim, naprawdę dobrym semestrze na mojej kochanej uczelni, nastał semestr czwarty – kompletna porażka. Szczęśliwie, są już wakacje, kolejny semestr za mną i nie będę tutaj wylewał swoich nieprzyzwoitych opinii na temat tego, co działo się przez ostatnie 5 miesięcy, bo nie ma co sobie niepotrzebnie podnosić ciśnienia. Pozwolę tylko zacytować sobie mojego kolegę, który z ulubowaniem siedział ze mną nocami, aby uczyć się fascynujących zagadnień „informatyki”: „Czuję się jakbym ładował gnój na rozrzutnik”, późniejsze wersje tegoż powiedzonka zawierały wzmianki o rzadkim gównie i nieszczelnych widłach itd. ;)

Przedmioty, na które miałem *przyjemność* uczęszczać to:

  • Bazy danych – przedmiot, który miał za zadanie pokazać mi, że bazy danych, to wcale nie sklejanie SQL-a w celu zrobienia prostej aplikacji webowej i że stoi za tym naprawdę głębsza filozofia. Niestety, nie udało się to. Nadal wydaje mi się, że to paskudny i mało twórczy temat. Dodam jeszcze, że prowadzącemu należy się wielki szacunek za zaangażowanie, a wykład był niezły, ale o całkiem niestrawnej porze.
  • Sieci komputerowe – wiązałem z tym duże nadzieje.. wyszło jak zwykle. Niezrozumiały wykład (chaos, kłopoty z polszczyzną, błędy ortograficzne na slajdach), całkiem absurdalne kolokwia i momentami przyzwoite laboratoria, na których można było się zabawić sprzętem CISCO i Linuksem. Zaletą tego przedmiotu jest to, że zachęcił on mnie do przestudiowania tematów, które zawsze chciałem zgłębić. Szkoda, że musiałem to zrobić sam, a nie mogłem wysłuchać tego od kompetentnego prowadzącego.
  • Programowanie zdarzeniowe – szkoda gadać, znowu przedmiot, który nazywa się „Programowanie <coś>”, a tak naprawdę było to „Składnia Javy w nieprezycyjnym ujęciu”. Nic o technikach, wzorcach, czy czymś niezwiązanym bezpośrednio z językiem. Do tego wykładowca.. mój nieulubiony. Projekt był prosty i (u mojego prowadzącego) nie trzeba było się starać. Zmarnowany czas.
  • Podstawy badań operacyjnych – tu już kompletnie szkoda słów.. Niby sporo o algorytmice (dynamiki, liniowe etc.), tak naprawdę zero konkretów, implementacja – oczywiście tylko na kartce. Niestety nic się na tym przedmiocie nie nauczyłem, może kilku ogólników. Laboratoria – fatalne! Wpisywanie danych do programu i przepisywanie wyników na kartkę – może to był kurs na sekretarkę?
  • Podstawy autmatyki – kompletna masakra. Jakieś równania różniczkowe, całki, liczby zespolone, transformaty. Ktoś się spodziewał, że to jest właśnie automatyka? Cel przedmiotu pozostał do teraz dla mnie zagadką. Chyba nikt, kto pracuje w zawodzie nie zajmuje się takim teorytezowaniem, bo i po co? Zawiodłem się, to miała być politechnika, a nie nierealna teoria. Szczęśliwie, przygodę z tym przedmiotem skończyłem po trzecim wykładzie, na koniec kilka nocy czytania skryptu i wpis w indeksie jest :)
  • Zaawansowane programowanie w C++ – to zdecydowanie przedmiot na osłodę tego bagna goryczy. Bardzo dobrze prowadzony i przygotowany. Dużo o wzorcach, technikach, bibliotekach. Przedmiot ten dokonał niemożliwego – przekonał mnie do C++! Projekt zespołowy – bardzo fajny (i wciąż będzie rozwijany!). Na cztery semestry, to jest trzeci przedmiot, który mi się podobał (świetny wynik, co?). Trzeba jeszcze dodać, że to przedmiot obieralny.. a powinien być obowiązkowy na drugim semestrze.

Z dobrych wieści tego semestru, to fakt, że dostałem się na specjalizację ISI – inżynieria systemów informatycznych. Oczywiście wybór nie był duży – druga specjalizacja to SID – systemy informacyjno decyzyjne, która składa się z przedmiotów pokroju badań operacyjnych i automatyki. Co wziąć, było zatem dla mnie całkowicie jasne. Swoją drogą, to ciekawe czym mają się zajmować ludzie po automatyce/badaniach operacyjnych na mojej uczelni, skoro mają bardzo silną i dobrze przeszkoloną konkurencję z SGH i MEiL. Moim zdaniem ta specjalizacja to jakaś kpina, bo po co uczyć ludzi kupy niekonkretnych rzeczy? Przecież nic przydatnego w ten sposób się nie nauczą. Ten ktoś, kto układał plan modelowy dla mojego kierunku pewnie jest uber haupt profesorem.. ale jakoś tego nie widać.

Na sam koniec moich fali moich narzekań.. Jeśli ktoś z Czytelników dopiero będzie wybierał się na studia, to:

  • Upewnij się, że Twoja uczelnia ma jakąś renomę wśród pracodawców
  • Upewnij się, że nie trzeba tam zbyt dużo robić

Bo jak powiedział kiedyś ktoś mądry – studia są po to, żeby pracodawca wiedział, że ci się chce. Wszystkiego co ciekawe, przydatne i ważne będziesz musiał nauczyć się sam, w domowym zaciszu. I to jest święta prawda.

4 miesiące

Na początku przepraszam wszystkich Czytelników za 4 miesiące ciszy. Moje wytłumaczenie nie będzie oryginalne – nocne „rajdy” na studiach skutecznie odbierały mi chęci do pisania na blogu. Właśnie skończył się 3 semestr mojej edukacji, pora skonfrontować go z oczekiwaniami z początku tego semestru, które można przeczytać w poprzedniej notce.

Zacznę od złego, czyli co mnie zawiodło. Przedmiot Algorytmy i struktury danych okazał się przedmiotem bardzo słabym, który nawet minimalnie nie spełnił moich oczekiwań. Cóż, sam przedmiot mało miał wspólnego z algorytmami (zawierał w zasadzie tylko struktury danych). Podsumuję go krótko – warto mu zmienić nazwę na Implementacja listy n-kierukowej w 100 smakach. Dacie wiarę, że będąc na trzecim semestrze informatyki, wiele ludzi ma problem z implementacją listy w C++? Nie? Pomyślcie teraz, że oni kiedyś będą z Wami pracować :) Architektura komputerów okazała się z kolei przedmiotem bardzo ciekawym, który zainteresował mnie na swój sposób, mimo że nigdy nie czułem żyłki do programowania niskopoziomowego, brawa za pierwszy sukces mojego wydziału! Fizyczne podstawy przetwarzania informacji, no cóż, niestety fizyka kwantowa nie jest moją mocną stroną, a cały przedmiot zaliczyłem na 4.5 nie mając za bardzo pojęcia „o co ch.. odzi”. Metody probabilistyczne i statystyka to ostatni przedmiot z cyklu matematyka i ku zaskoczeniu okazał się bardzo przyjemny – gratulacje dla mega sympatycznej prowadzącej. Podstawy telekomunikacji, żeby nie było za różowo, to kolejna sieczka… jakieś systemy, prędkości połączenia, dziwne laboratoria.. lekka psychodela, na szczęście dało się ją zaliczyć :) Teoria sygnałów i informacji, gdyby nie sygnały i związane z nią laborki (znowu przepinanie kabli bez najmniejszej wiedzy po co, gdzie i jak) byłaby znośna. Przy okazji chciałbym wspomnieć o interesującym zjawisku na moim wydziale, które wydaje mi się, popularne na całej Politechnice. Mianowicie, wiele kolokwiów, które tutaj piszę nie są trudne z powodu złożoności materiału. Są trudne z powodu bardzo małej ilości czasu. Czy to nie jest absurdalne? Słyszę ciągłe gadanie „prawdziwy inżynier to, prawdziwy inżynier tamto…”. Chciałoby się spytać, czy „prawdziwy inżynier” powinien dużo wiedzieć, czy szybko (ale mało) wiedzieć? Czy programiści piszą swoje programy na czas (liczony w minutach), czy może jednak powinny być one przemyślane? Informatyka to nie zawód-wyścigi, to zawód w którym liczy się rzetelna wiedza. Po co więc testuje się studentów z tego, jak bardzo oklepali 4 schematy w domu? Może warto rozwiązywać problemy twórcze, wymagające wiedzy, a nie sprawnej ręki? Ok, starczy narzekania, teraz deser w oddzielnym akapicie.

Systemy operacyjne, to przedmiot marzenie! Tematyka wykładu okazała się dla mnie ciekawa, wręcz porywająca. Na pewno by tak dobrze nie było, gdyby nie Pan dr inż. Tomasz Jordan Kruk który niesamowicie zainteresował mnie zagadnieniem systemów operacyjnych, a także zmienił mój sposób postrzegania informatyki jako dziedziny nauki i (wydaje mi się) znacząco wpłynął na ścieżkę kariery, którą obiorę na przyszłość. Przedmiot w tej realizacji, to dobry powód aby pójść na wydział Technik Informacyjnych. Dziękuję bardzo!

Co jeszcze ciekawego zdarzyło się przez te 4 miesiące? Niestety w tym roku nie udało mi się wystartować w Imagine Cup, a szkoda! Mój wydział niestety kontynuuje tradycję nie zebrania nawet jednej drużyny… Oprócz tego zmieniłem swoją stację roboczą na Dell Studio 1555, który zastąpił ASUS-a F3Tc, który rozsypał się po 2.5 roku użytkowania (oczywiście zaraz po końcu gwarancji :)). Po tym okresie użytkowania z czystym sercem NIE polecam nikomu ASUS-ów. Na nowym komputerze posiadam dual-boot Windows 7 i dystrybucję Linuksa.

W najbliższym czasie rozpocznę nowy semestr, który zapowiada się dobrze, z powodu małej ilości zajęć, mianowicie:

  • Podstawy automatyki i Podstawy badań operacyjnych – dwa przedmioty które związek z informatyką mają raczej niewielki.. i nie wiem czego się po nich spodziewać. Raczej nie będą dla mnie porywające, ale kto wie ;)
  • Sieci komputerowe – ot nazwa mówi sama za siebie, myślę że przedmiot do przełknięcia, mimo że nie leży w kręgu moich „najulubieńszych” zainteresowań
  • Programowanie zdarzeniowe – czyli mówiąc krótko programowanie w Javie; przedmiot w którym do zrealizowania jest projekt – liczę na coś ciekawego! (choć wykłady zapowiadają się na słabe..)
  • Bazy danych – informatyki ciąg dalszy – myślę, że przedmiot mógłby być ciekawy, ale.. wykład zapowiada się słabo – może laboratoria uratują sytuację?
  • Zaawansowane programowanie w C++ – przedmiot obieralny, który ma mi w tym semestrze umilić życie :) – do zrobienia jest projekt przy użyciu biblioteki Boost – projekt drużynowy, co dodatkowo zwiększa oczekiwania, oby było dobrze!
  • No i WF – siatkówka oraz lektorat – znów niemiecki

Co do bloga, to plany oczywiście są takie, żeby pisać więcej. Bardzo możliwe, że będą to tematy niekoniecznie dotnetowe, ale o tym – może wkrótce. Do usłyszenia więc w kolejnej notce, wracam do wylegiwania się!

Koniec wakacji = 3 semestr nauki

10 dni temu moje wakacje dobiegły końca. Przyznam się szczerze do mało zaskakującej rzeczy – wakacje to mój ulubiony okres w ciągu całego roku :) Te były dosyć wyjątkowe, ponieważ w ciągu tych 90 dni przeżyłem wiele wyjątkowych „przygód” – na początku byłem mocno zaangażowany w budowę nowego domu. Praca fizyczna, która domeną informatyka nie jest, okazała się wyjątkowo ciężka, momentami nużąca, ale widoczność efektów „gołym” okiem daje wiele satysfakcji. Nieco później zdobyłem w firmie Ortega pierwsze, naprawdę poważne doświadczenie zawodowe – jako programista ASP.NET. Było to naprawdę interesujące i skłaniające do różnych refleksji wydarzenie. Zarobione ciężką pracą pieniądze umożliwiły mi spełnienie małego marzenia – w końcu spróbowałem żeglarstwa i jestem pewien, że to nie był ostatni raz (kolejny mam nadzieję już za rok!). Dopełnieniem ekstremalnych wrażeń była ponad tygodniowa wycieczka do Zakopanego (uwielbiam góry!). Tu i tu są dwa małe PhotoSynthy zrobione przeze mnie, podczas tej wyprawy, polecam :) W tak zwanym międzyczasie trochę programowałem dla siebie, imprezowałem, relaksowałem się lub dużo się leniłem. Ogólnie – super wakacje :)

Wakacje jak wszystko co dobre – kończą się. Przyszedł czas zmierzyć się z 3 semestrem na mojej kochanej Elce… Po mega frustracji związanej z semestrem 2 nie spodziewam się wiele, choć z drugiej strony, chyba gorzej być nie może. W tym semestrze czeka mnie:

  • Algorytmy i struktury danych – nazwa mówi sama za siebie – programowanie struktur danych w C++. Niby nic szczególnego, jednak po dużej dawce fizyki z semestru 2 niezwykle cieszy mnie ten przedmiot. Chciałbym tylko, żeby wbrew konspektowi przedmiotu, było na nim więcej algorytmiki niż struktur danych, no ale zobaczymy jak to wyjdzie.
  • Architektura komputerów – czyli o tym, jak to wszystko tak naprawdę działa :) Nie jest to może mój „konik”, ale wydaje się być ciekawie i dosyć trudno. Trochę programowania w C, trochę w assemblerze, sporo teorii.
  • Fizyczne podstawy przetwarzania informacji – fizyka – półprzewodniki i rzeczy z tym związane. Bez rewelacji, mam nadzieję, że łatwo pójdzie ;)
  • Język niemiecki – bez komentarza, lektorat na uczelni potrafi być uciążliwy..
  • Metody probabilistyczne i statystyka – na szczęście to już „ostatnia matematyka” na moich studiach, z resztą ten dział matematyki jest dla mnie najbardziej interesujący, choć mam przeczucie, że uczenie się tego w wersji akademickiej będzie „fuuuuuuj”.
  • Podstawy telekomunikacji – przedmiot zagadka, trochę nie wiem czego się po nim spodziewać. Osobiście mam nadzieję na odrobinę ciekawych informacji.
  • Systemy operacyjne – przedmiot wziąłem dodatkowo, do modelowego planu zajęć. Wydaje się być najtrudniejszym przedmiotem ze wszystkich. Sporo teorii i hackowania jądra UNIX-a. Temat jednak bardzo mnie zainteresował!
  • Teoria sygnałów i informacji – niejako kontynuacja Podstaw elektroniki z zeszłego semestru, pozostaje tylko mieć nadzieję, że będzie ciekawsze i/lub łatwiejsze. Ogólnie sporo fizyki.

Jak to będzie? Czas pokaże. Jestem nie najgorszej myśli, ale różnie to bywa. Do boju więc i życzę wszystkim miłego roku pracy do następnych wakacji :)

Spóźnione wrażenia z Bloggers Underground #2.5

Minęło już sporo czasu od spotkania bloggerów około technologii MS – Bloggers Underground #2.5, niestety leń i sporo zajęć (ale raczej to pierwsze) powstrzymały mnie od opisania swoich wrażeń na gorąco, jednak postaram się to trochę nadrobić. „Tradycja” spotkań z cyklu BU ma już rok i w zeszłym roku też miałem okazję uczestniczyć w owym spotkaniu i wspominam je bardzo ciepło. W tym roku było nieco inaczej. Inaczej, ale nie gorzej. Tym razem spotkanie było mniej kameralne (choćby ze względu na miejsce – Klub Piekarnia), jednakże tak jak rok temu nie brakowało ciekawych prezentacji i bardzo, bardzo gorących dyskusji. Co prawda nie dotrwałem do samego końca imprezy (musiałem wybyć koło 24), ale już wtedy wraz z znacznie uszczuploną liczbą uczestników, dyskusje się bardzo rozkręciły. Równolegle do spotkania BU, w klubie odbywała się jeszcze inna impreza – Communities@Night, na której można było spotkać bardzo wiele osób zaangażowanych w różny sposób w społeczności produktów MS. Była to świetna okazja do zawarcia nowych znajomości, w końcu nie na co dzień jest się w klubie np. z szefem MS Polska – Jackiem Murawskim :) Bardzo cieszę się z możliwości spotkania i pogadania na tematy mało lub wcale techniczne z osobami, które jednak zwykle widzę i spotykam na czysto technicznych wydarzeniach. Uważam, że spotkania z cyklu BU (mam nadzieję na kolejne!!) są same w sobie zachęta, aby zacząć (lub dalej pisać) bloga ;) Dziękuję bardzo wszystkim organizatorom – za organizację i zaproszenie mnie i czekam na następne spotkania :)

Zapytanie LINQ vs metody LINQ

Wszyscy chyba zdają sobie sprawę z tego, jakim dobrodziejstwem jest LINQ, które pojawiło się dosyć dawno, wraz z .NET 3.0 3.5. Jak wiadomo LINQ oferuje trochę nowych słów kluczowych oraz trochę metod – i tu pojawia się pytanie  – czym różni się zapis za pomocą słów kluczowych od zapisu „metodowego”? Szczególnie interesujące zdaje się być to, czy któryś z zapisów powoduje jakiś narzut wydajnościowy.

Nie będę budował napięcia i od razu odpowiem – query syntax w zasadzie nie różni się niczym od zapisu za pomocą extension methods. W praktyce, zapytanie zapisane za pomocą słów kluczowych jest tłumaczone podczas procesu kompilacji na coś, co CLR świetnie rozumie – metody. Microsoft, tak jak wielu programistów, preferuje zapis „zapytaniowy” nad „metodowym” ze względu na czytelność. Moim zdaniem jest to sprawa mocno dyskusyjna, dla mnie często dużo bardziej czytelniejsze jest używanie extension methods. Ponadto, niestety nie wszystko zapisywalne za pomocą extension methods da się zapisać za pomocą słów kluczowych, ale o tym za moment w przykładach.

Przykład 1

int[] numbers = { 5, 10, 8, 3, 6, 12 };

IEnumerable numQuery1 =
    from num in numbers
    where num % 2 == 0
    orderby num
    select num;

Taki kod równoważny jest (i jednocześnie tłumaczony w procesie kompilacji na następujący):

IEnumerable numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n);

Czy ten drugi nie wydaje się Wam czytelniejszy? ;) Nietrudno jednak stworzyć przykład w którym query syntax będzie bardziej wymowne.

Przykład 2

var personQuery1 = from person in people
                   let fullname = person.Name + " " + person.Surname
                   select new { Name = fullname, Age = person.Age };

var personQuery2 = people
                   .Select(person => new { Name = person.Name + " " + person.Surname, Age = person.Age });

W pierwszej linijce widzimy jak przydatne (dla czytelności) jest słowo kluczowe let pozwalające na tymczasową definicję nowej zmiennej (więcej o słówku let tu i tu na blogu Maćka Aniserowicza). Zapis za pomocą extension methods nie jest już taki ładny (choć to też zapewne sprawa gustu). Ostatni przykład zaprezentuje nam, że nie wszystko da się zrobić za pomocą query syntax.

Przykład 3

int[] numbers = { 5, 10, 8, 3, 6, 12 };
var nums = numbers.Skip(3).Take(2).ToList();

Efektu wywołania takich metod nie otrzyma się za pomocą zapytania.

Podsumowanie

Wszystko co da się napisać za pomocą query syntax da się zapisać za pomocą extension methods. Odwrotnie już tak niestety nie jest. Często zapis za pomocą słów kluczowych jest dużo czytelniejszy (szczególnie przy używaniu let, group, join), natomiast metody dają nam szersze możliwości. Wybór więc powinien zależeć od osobistego stylu i zdania na temat tego, co jest bardziej wymownie. Nieważne jednak czy korzystasz z metod, czy z zapytań, powinieneś robić to świadomie, aby nie tracić na wydajności – polecam przeczytać drugi z linków do blogu Maćka powyżej.

Prosty sposób na uaktualnienie GUI za pomocą BackgroundWorkera

Podczas pisania aplikacji okienkowych często zdarza się, że nasz program pobiera duże ilości danych, a następnie wyświetla je użytkownikowi. Niezwykle denerwującą sytuacją jest, gdy całość danych wyświetla się „na raz”. Jeśli nie „zamraża” to interfejsu użytkownika i jeszcze do tego pisze coś w stylu „Praca w toku” to jeszcze pół biedy, gorzej jak użytkownik pozostaje z okienkiem z uroczym dopiskiem „Nie odpowiada”. Wielkie szanse, że zirytowany taką sytuacją wyłączy naszą aplikację i zgłosi nam błąd – że nie działa :) Warto jednak zrobić coś jeszcze lepszego, niż zwykłe ostrzeżenie użytkownika, że troszkę poczeka.

Moim zdaniem najlepiej jest wprowadzić do aplikacji pasek postępu (choć nie zawsze jest to możliwe, bo czasem nie wiadomo ile danych przybędzie) i przede wszystkim – podzielić dane na paczki i prezentować dane progresywnie.

Jak to zrobić?

Najprostszym i przyjemnym sposobem jest użycie klasy BackgroundWorker, ale zacznijmy od początku. W celach demonstracyjnych stworzymy proste okienko, które będzie zawierało ListBox – do prezentacji wyników, przycisk „Start” oraz ProgressBar. Przejdźmy teraz do kodu:

private BackgroundWorker backgroundWorker = new BackgroundWorker();
private const int Results = 40;
private const int MaxPartCount = 5;

public PartialUpdateWindow()
{
    InitializeComponent();

    //initializing backgroundWorker
    backgroundWorker.DoWork += backgroundWorker_DoWork;
    backgroundWorker.ProgressChanged += backgroundWorker_ProgressChanged;
    backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
    backgroundWorker.WorkerReportsProgress = true;
}

Kod chyba jest bardzo wymowny. Tworzymy nową instancję BackgroundWorkera, dla prostoty przykładu ustalamy ile będzie wyników i ile ma liczyć maksymalna paczka danych do wyświetlenia. Następnie przypisujemy odpowiednie zdarzenia – DoWork – czyli to co ma zostać wykonane, ProgressChanged – zdarzenie odpalane w wątku GUI, które wyświetla wyniki, RunWorkerCompleted – odpalane po zakończeniu DoWork. Ostatnia linijka jest istotna, bo domyślnie ustawiona jest na false i wywołanie ProgressChanged skończy się wyjątkiem. Przejdźmy teraz do implementacji poszczególnych metod.

private void startButton_Click(object sender, RoutedEventArgs e)
{
    resultsListBox.Items.Clear();
    startButton.IsEnabled = false;
    backgroundWorker.RunWorkerAsync(Results);
}

Na początek funkcja podpięta pod przycisk która odpali naszego BackgroundWorkera. Jak widzimy do metody RunWorkerAsync, która uruchamia wątek możemy przekazać opcjonalnie argument. Może być on dowolnego typu i może być tylko jeden. W razie potrzeby, można zawsze przesłać tablicę lub inny kontener.

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    int percentageComplete;
    List tempResults = new List();
    int partsCount;

    var random = new Random();

    for (var i = 1; i <= Math.Ceiling((int)e.Argument / (double)MaxPartCount); i++)
    {
        Thread.Sleep(random.Next(500, 1000)); //simulating data fetching

        if (i * MaxPartCount <= (int)e.Argument)
            partsCount = MaxPartCount; //send maximum
        else
            partsCount = (int)e.Argument - i * MaxPartCount; //send remaining

        for (var j = 0; j < partsCount; j++)
            tempResults.Add(random.Next());

        percentageComplete = (int)(((double)(i * MaxPartCount) / (int)e.Argument) * 100);

        backgroundWorker.ReportProgress(percentageComplete, tempResults);
    }
}

Teraz główna część naszego przykładu, czyli metoda podpięta pod DoWork. W środku tej metody tworzymy pętlę, która przebiega odpowiednią ilość razy (sufit ze wszystkich wyników podzielonych na maksymalna ilość części). Jak widzimy możemy się odwołać do przesłanego argumentu poprzez e.Argument. Musimy jednak stosować rzutowanie, gdyż e.Argument jest typu object (to dosyć oczywiste). W dalszych linijkach generujemy w losowym odstępie czasu po partsCount (równa MaxPartCount albo to co nie zostało jeszcze wysłane) liczb losowych, a także obliczamy procent wykonanej roboty. Kluczową częścią metody jest jej ostatnia linijka, która wywołuje w wątku GUI metodę podpiętą pod ProgressChanged. Do metody tej przekazujemy procent wykonanej pracy oraz znowu mamy opcjonalnie do przekazania jeden argument (ponownie typu object). To umożliwia nam sprytne użycie pozyskanych częściowych wyników - wysłanie ich do GUI. Dalsza część to już oczywistość:

private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    completeProgressBar.Value = e.ProgressPercentage;
    foreach (var item in (List)e.UserState)
        resultsListBox.Items.Add(item);
}

private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    startButton.IsEnabled = true;
}

W backgroundWorker_ProgressChanged uaktualniamy stan ProgressBara oraz dodajemy pozyskane elementu do ListBoksa. Jest to możliwe gdyż, jak już wspomniałem, metoda ta wywoływana jest w tym samym wątku co GUI. Warto zwrócić uwagę na przekazany argument dostępny pod e.UserState - znów konieczne jest rzutowanie. Po skończonej pracy odblokowywujemy przycisk start i to tyle.

Podsumowanie

Widzimy, że rozwiązanie problemu częściowego uaktualniania interfejsu za pomocą BackgroundWorkera jest bardzo łatwe. To jest niewątpliwy plus. Ten sposób ma też (jak chyba wszystko ;)) jednak swoje wady. Po pierwsze, widać w kodzie, że w wielu miejscach musimy stosować rzutowanie z typu object na inny typ. Nie jest to operacja bezpieczna i przy jakichkolwiek zmianach w kodzie należy pamiętać aby pozmieniać też rzutowania w innych miejscach. Innym minusem jest utrudnione oddzielnie widoku od logiki, gdyż klasa BackgroundWorker pozostaje mocno powiązana z GUI.

Konicząc mój wywód - jeśli tworzysz małą aplikację lub pogodzenie się z minusami BackgroundWorkera nie jest szkodliwe dla Ciebie, ani dla tworzonego kodu - jak najbardziej polecam to rozwiązanie, bo po co pisać kod, który stworzył już ktoś wcześniej?

Producent i konsument – przykład użycia słowa kluczowego lock

Niejednokrotnie podczas pisania aplikacji napotyka się na sytuację gdy jedna metoda produkuje pewne dane, inna natomiast w pewien sposób je konsumuje. Czasem dobrym pomysłem jest, w przypadku gdy produkowane dane są w pewien sposób podzielne na części, wykonywać produkcję i konsumpcję w równoległych wątkach. Tutaj pojawia się istotny problem z zagadnienia wielowątkowości – synchronizacja. Oba (wszystkie) wątki współdzielące dany zasób muszą z niego korzystać w pewien ustalony sposób, tak aby w danej chwili korzystał z niego tylko jeden z wątków. W uproszczeniu, zapewnienie tego stanu nazywamy synchronizacją. Problem ten jest na tyle niebanalny i powszechny, że platforma .NET wspomaga synchronizację za pomocą wbudowanych mechanizmów. Jednym z nich jest słowo kluczowe lock. Użycie słowa kluczowego lock wygląda tak:

 lock(somethingSharedToLock)
{
     // operations on the locked object
}

Jak widać składnia jest bardzo prosta, ale co tak właściwie daje nam lock? Użycie słowa kluczowego lock gwarantuje nam, iż żaden inny wątek nie będzie ingerował w zablokowany obiekt podczas wykonywania instrukcji z nawiasu klamrowego. Natomiast jeśli obiekt jest zablokowany przez inny wątek, to kolejny lock będzie cierpliwie czekał na zwolnienie obiektu i dopiero wtedy go zablokuje. Pięknie, prawda? :) Oczywiście lock, jak wszystko – ma swoje wady. Częste i nierozważne lockowanie może doprowadzić do tzw. zakleszczenia (ang. deadlock). Starczy tej teorii, przejdźmy do przykładu (przykład z całą pewnością nie pokazuje best practices tworzenia kodu, ale chodziło mi o maksymalne uproszczenie przykładu).

Stwórzmy sobie prostą klasę producenta:

class Producer
{
public bool IsFinished
{
    get;
    set;
}
public List List
{
    get;
    set;
}
public Producer()
{
    List = new List();
    IsFinished = false;
}
public void Produce()
{
    Random random = new Random();

    for (int i = 0; i < 20; i++)
    {
        List.Add(random.Next(0, 20));
        Console.WriteLine("Producer[{0}]={1}", i, List[i]);
        Thread.Sleep(500);
    }
    IsFinished = true;
    Console.WriteLine("Producer has finished his work");
}
}

Klasa ta, jak widać, w konstruktorze przyjmuję listę, a następnie po wywołaniu Produce() wpisuje do tej listy losowe liczby całkowite z przedziału <0,20) wypisując je przy okazji. Klasa posiada pole które poinformuje nas, że praca została zakończona. Usypianie w metodzie produkującej umożliwi nam zaobserwowanie pewnej ciekawej rzeczy, ale o tym dalej.

Klasa konsumenta:

class Consument
{
    private Producer producer;
    public Consument(Producer producer)
    {
        this.producer = producer;
    }
    public void Consume()
    {
        int currentElement = 0;
        int elementCount = 0;

        while (true)
        {
              elementCount = producer.List.Count;

              if (producer.List.Count > currentElement)
              {
                   producer.List[currentElement] += 2;
                   Console.WriteLine("Consument[{0}]={1}", currentElement, producer.List[currentElement]);
                   currentElement++;
              }
              if (producer.List.Count == currentElement && producer.IsFinished)
                   break;
         }
         Console.WriteLine("Consument has finished his work");
    }
}

Konsument, w konstruktorze przyjmuje producenta, a po wywołaniu Consume(), o ile pojawiło się coś nowego zwiększa wartość każdego elementu o 2, a następnie go wypisuje. Metoda kończy swoje działanie w momencie, gdy Producent da o tym znać i wszystkie elementy zostaną zmienione.

Pozostaje nam odpalić produkcje i konsumpcje w oddzielnych wątkach:

private static void Main(string[] args)
{
     Producer producer = new Producer();
     Consument consument = new Consument(producer);

     Thread producerThread = new Thread(new ThreadStart(producer.Produce));
     Thread consumentThread = new Thread(new ThreadStart(consument.Consume));

     producerThread.Start();
     consumentThread.Start();
     producerThread.Join();
     consumentThread.Join();
}

Wynik działania programu (przykładowy!!):

Producer[0]=0
Consument[0]=2
Producer[1]=4
Consument[1]=4
Producer[2]=17
Consument[2]=17
Producer[3]=8
Consument[3]=8
Producer[4]=0
Consument[4]=2
Producer[5]=16
Consument[5]=16
Producer[6]=6
Consument[6]=6
Producer[7]=13
Consument[7]=13
Producer[8]=17
Consument[8]=19
Producer[9]=9
Consument[9]=11
Producer[10]=15
Consument[10]=17
Producer[11]=14
Consument[11]=14
Producer[12]=11
Consument[12]=13
Producer[13]=3
Consument[13]=5
Producer[14]=15
Consument[14]=15
Producer[15]=13
Consument[15]=13
Producer[16]=16
Consument[16]=18
Producer[17]=5
Consument[17]=7
Producer[18]=2
Consument[18]=2
Producer[19]=14
Consument[19]=16
Producer has finished his work
Consument has finished his work

Ups! Widzimy, że w wielu przypadkach (1, 2, 3, 5, 6, 7, 11, 14, 15, 18 - czyli w połowie wszystkich) wartość producenta nie została zmieniona, tak jakbyśmy chcieli. Czemu się tak stało? Otóż produkcja kolejnych elementów trwa dłużej niż konsumpcja (poprzez wspomniane wcześniej Thread.Sleep(500)) co powoduje iż w pewnych momentach dana jest "jeszcze" tworzona, a próbuje już być skonsumowana, co powoduje iż proces konsumpcji (dodanie do wartości 2) zostaje pominięte. Uniknięcie tej katastrofy jest możliwe dzięki wspomnianemu lockowi. Wystarczy napisać:

lock (((ICollection)List).SyncRoot)
{
    List.Add(random.Next(0, 20));
    Console.WriteLine("Producer[{0}]={1}", i, List[i]);
    Thread.Sleep(500);
}

oraz:

lock (((ICollection)producer.List).SyncRoot)
{
    elementCount = producer.List.Count;

    if (producer.List.Count > currentElement)
    {
        producer.List[currentElement] += 2;
        Console.WriteLine("Consument[{0}]={1}", currentElement, producer.List[currentElement]);
        currentElement++;
    }
    if (producer.List.Count == currentElement && producer.IsFinished)
        break;
}

To zapewni nam prawidłowe działanie kodu (jak nie wierzycie - sprawdźcie sami :)). A co tak właściwie się stało? Kolekcja w momencie produkowania jest na chwilę blokowana, a więc konsumpcja jest wtedy wstrzymana, gdy produkcja się skończy, konsumpcja zabiera kolekcję dla siebie. Dzięki temu działanie jest zgrabnie zsynchronizowane. Dla kolekcji, w celu synchronizacji należy używać właściwości SyncRoot która jest wymagana przez interfejs ICollection. Wszystkie inne obiekty możemy lockować w sposób bezpośredni (lock(someObject)) chyba, że coś (dokumentacja) podpowiada nam inaczej ;)

To tyle w tej kwestii, dla zainteresowanych polecam poczytać Thread Synchronization oraz How to: Synchronize a Producer and a Consumer Thread.

Pełny listing:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;

namespace ProducentConsumerExample
{
    class Example
    {
        private static void Main(string[] args)
        {
            Producer producer = new Producer();
            Consument consument = new Consument(producer);

            Thread producerThread = new Thread(new ThreadStart(producer.Produce));
            Thread consumentThread = new Thread(new ThreadStart(consument.Consume));

            producerThread.Start();
            consumentThread.Start();

            producerThread.Join();
            consumentThread.Join();
        }
    }

    class Producer
    {
        public bool IsFinished
        {
            get;
            set;
        }

        public List List
        {
            get;
            set;
        }

        public Producer()
        {
            List = new List();
            IsFinished = false;
        }

        public void Produce()
        {
            Random random = new Random();

            for (int i = 0; i < 20; i++)
            {
                lock (((ICollection)List).SyncRoot)
                {
                    List.Add(random.Next(0, 20));
                    Console.WriteLine("Producer[{0}]={1}", i, List[i]);
                    Thread.Sleep(500);
                }
            }

            IsFinished = true;

            Console.WriteLine("Producer has finished his work");
        }
    }

    class Consument
    {
        private Producer producer;

        public Consument(Producer producer)
        {
            this.producer = producer;
        }

        public void Consume()
        {
            int currentElement = 0;
            int elementCount = 0;

            while (true)
            {
                lock (((ICollection)producer.List).SyncRoot)
                {
                    elementCount = producer.List.Count;

                    if (producer.List.Count > currentElement)
                    {
                        producer.List[currentElement] += 2;
                        Console.WriteLine("Consument[{0}]={1}", currentElement, producer.List[currentElement]);
                        currentElement++;
                    }
                    if (producer.List.Count == currentElement && producer.IsFinished)
                        break;
                }
            }

            Console.WriteLine("Consument has finished his work");
        }
    }
}

Wakacje!

Uf, w poniedziałek zdałem indeks do dziekanatu – koszmarny drugi semestr dobiegł końca. W lutym pisałem, a właściwie narzekałem na swoje studia. Po drugim semestrze niestety nie mogę powiedzieć nic lepszego, a w zasadzie dużo więcej złego. Po raz pierwszy spotkałem się ze sporą dawką chamstwa prowadzących, słynnego ze studyjnych anegdotek, którzy przy okazji nie wykazywali się byłyskotliwością, wiedzą czy umiejętnością prowadzenia zajęć. Wystarczy powiedzieć, że przedmiot na który patrzyłem z nadzieją – programowanie obiektowe – miewało frekwencję sięgającą 5%, co mówi samo za siebie. Niestety mam wrażenie, że przez ten cały rok niewiele (nic przydatnego..?) się nauczyłem. Udało mi się go zamknąć ze średnią, o ile moje obliczenia mnie nie mylą, 4.74 co jest chyba przystępnym wynikiem. Ale dość już narzekania, na drugi rok patrzę znowu z nadzieją, oby tym razem się postarali ;).

W te wakacje mam zamiar głównie wypocząć, gdyż ostatnie 9 miesięcy solidnie mnie wymęczyło. Czym jednak byłyby wakacje bez klepania kodu ;) Przy okazji chciałbym zrealizować kilka zleceń, więc jakby ktoś coś miał, to zapraszam do działu kontakt ;).

Na pierwszy rzut oka widać zmiany na blogu – przesiadłem się, ze swojej własnej platformy na BlogEngine.net. Powód? Dosyć prozaiczny – moja platforma była bardzo mocno niedopracowana, co bardzo zniechęcało mnie do pisania. Obiecuję sobie jednak dokończyć ją kiedyś i wtedy wrócić „na własne”. Czemu natomiast BlogEngine? Przyjrzałem się najpopularniejszym systemom napisanym w .NET (BlogEngine.NET, SubText, dasBlog) i zdecydowanie BE najbardziej przypadł mi do gustu (największe możliwości konfiguracji i edycji).

To tyle na ten wpis. Po raz kolejny obiecuję pisać więcej technicznych notek.

Steve Ballmer – spotkanie ze społecznością / Imagine Cup 2010 w Polsce!

23 kwietnia, o godzinie 10 w hotelu Sheraton w Warszawie odbyło się spotkanie Steve’a Ballmera ze społecznościami IT. Tak się złożyło, że dostąpiłem zaszczytu bycia zaproszonym na owe spotkanie :)

Steve’a Ballmera – prezesa MS nie trzeba chyba specjalnie przedstawiać. Wszyscy zorientowani w świecie Microsoftu kojarzą zapewne ekscentryczne wystąpienia Steve’a na różnych konferencjach. Miałem więc nadzieję, że i tym razem zobaczę coś niezwykłego, ponadto byłem zaciekawiony jego osobą samą w sobie. Jak więc było?

Na początek – nic tak szalonego jak na filmiku wyżej się nie stało, a szkoda ;) Całe spotkanie miało charakter krótkiej prezentacji Steve’a, a pozostała część to Q&A. Nie będę tu przytaczał pytań zadawanych przez publiczność – powiem tylko, że było ciekawie i bardzo zabawnie. Steve oczarował mnie swoją charyzmą i przemyślanymi za każdym razem odpowiedziami na pytania. Szkoda tylko, że spotkanie było tak krótkie.

Przy okazji swojej wizyty w Polsce, Steve ogłosił iż finały kolejnej edycji Imagine Cupa odbędą się w Polsce! Cóż, to doskonała okazja, żeby zabłysnąć w przyszłym roku, bo niestety w tym roku, swoje wystąpienie określiłbym tak ;)