Niekiedy trzeba zrobić coś czasochłonnego: operację, która nie zakończy się od razu, lecz zabierze zauważalny odcinek czasu. Dość często dotyczy to odczytu (lub zapisu) danych z miejsca, które nie musi być natychmiast dostępne: gniazdka sieciowego, międzyprocesowego potoku (pipe) czy w niektórych sytuacjach nawet pamięci dyskowej. Wówczas rzadko możemy pozwolić sobie na “powieszenie” programu na parę(naście/dziesiąt) sekund w oczekiwaniu, aż zlecona operacja się zakończy. W międzyczasie trzeba bowiem wykonywać też inne czynności, z aktualizacją interfejsu użytkownika na czele.
Typowy rozwiązaniem jest wtedy umieszczenie czasochłonnej czynności w osobnym wątku. Zdarza się jednak, że nie jest to jedyne wyjście. Niekiedy – na przykład przy korzystaniu z gniazd sieciowych w Windows API lub dowolnych strumieni w .NET – dysponujemy alternatywnym sposobem, którym jest zlecenie operacji asynchronicznej. Polega ono na żądaniu wykonania danego działania “w tle” wraz ze sposobem, w jaki chcemy odebrać informację zwrotną. W tym charakterze chyba najczęściej stosuje się funkcje typu callback, podawane – zależnie od języka – jako wskaźniki (C/C++), delegaci (Delphi, C#) lub obiekty implementujące ustalone interfejsy (Java). Po zakolejkowaniu takiego żądania program wykonuje się dalej bez żadnych przerw. Gdy zaś operacja zakończy się, nasz callback zostanie wywołany i w nim będzie można pobrać rezultaty zleconego zadania.
Brzmi całkiem nieźle, prawda? Właściwie można by powiedzieć, że to świetny sposób na uniknięcie stosowania tych strasznych wątków ;-) W praktyce trzeba jednak pamiętać o tym, że:
W sumie więc warto pamiętać o tym, że przy wprowadzaniu równoległości trzeba zawsze liczyć z dodatkowymi – nazwijmy to – “kwestiami do rozważenia” :] Unikanie tworzenia wątków za wszelką cenę nie musi zatem być najlepszym wyjściem, skoro koszt rozwiązania alternatywnego bywa podobny.
Jeśli chodzi Ci o BackgroundWorker w .NET to jest on realizowany za pomocą innego wątku. Właściwie każda operacja asynchroniczna opiera się na utworzeniu lub wykorzystaniu istniejącego już drugiego wątku. Wątków nie unikniesz ale możesz zapakować w przyjazny interface (jak chociażby BackgroundWorker :) ).
Ja uważam, że z aktualnymi trendami technologicznymi najlepsze jest właśnie mnożenie wątków :) Mówię oczywiście o aspekcie wydajnościowym, bo wiadomo, że im więcej wątków tym programista musi się bardziej nagłowić.
Powodem takiego mojego rozumowania jest to, że większość procesorów już jest dwurdzeniowych i żeby je w pełni wykorzystać to najlepiej stworzyć właśnie 2 wątki. W przyszłości może będzie jeszcze więcej rdzeni w jednym procesorze.
No ale nie można popadać ze skrajności w skrajność. Mnożenie wątków na siłę i tam gdzie nie trzeba może dodatkowo spowolnić program lub w najlepszym wypadku nie zmienić jego wydajności – a programista i tak się namęczy.
Programowanie to sztuka wyboru mniejszego zła :)
Hej
Tak się dzisiaj złożyło, że poruszyłeś nieco zagadnienie wielowątkowości, a akurat Jeff Atwood na swoim blogu poruszył podobny temat. Może Was, programistów gier, to nieco zainteresuje, więc pozwoliłem sobie podać adres: http://www.codinghorror.com/blog/archives/001073.html
Trochę o grafice 3D, o renderowaniu, ray tracing i wielowątkowość.
pozdrawiam
Powiedziałbym wręcz, że asynchroniczne wejście-wyjście to bardziej “prawidłowa” metoda. Na przykład co, jeśli używasz wejścia-wyjścia w sposób blokujący w osobnym wątku, a użytkownik zażąda w tym czasie przerwania operacji albo zakończenia programu. KILL?
Wątek przynajmniej można zabić, operacji asynchronicznej niekoniecznie (zależy od konkretnej sytuacji i implementacja).