Delphi - Wątki
Sun, 10 April 2005
Wstęp
Witam! Zapewne wielu z Was spotkało się kiedyś z syndromem "zawieszania" się programu np. podczas wykonywania skomplikowanej czynności. Tak naprawdę aplikacja wtedy działa, ale nie odbiera od systemu komunikatów, np. o konieczności odświeżenia okna, co jest szczególnie widoczne gdy formularz naszego programu przysłonimy (a następnie odsłonimy) jakimś innym oknem. Wówczas formularz naszej aplikacji staje się cały biały, a przy dłuższych tego typu "brakach reakcji" system oznacza program jako ten który "nie odpowiada". W rzeczywistości oznaczenie "nie odpowiada" jest tak naprawdę stwierdzeniem, iż nasz program nie odbiera komunikatów wysyłanych mu przez system. Co więc robić by uniknąć tego czarnego scenariusza ? Są na to dwie metody. Pierwszą metodą jest wstrzelenie instrukcji Application.ProcessMessages gdzieś w pętlę; tą która powoduje, że program się zawiesza. Druga, to bardziej skomplikowana metoda którą właśnie z tego względu się dzisiaj zajmiemy. Zadanie takie można powierzyć do rozwiązania wątkom. Co to są te wątki? Jak je tworzyć? Zapraszam do artykułu, w którym zgłębimy ich tajemnicę i postaramy się je oswoić.
Co to są te wątki?
Aby zrozumieć ich istotę wystarczy je wyobrazić sobie jako podprogramy, które działają prawie niezależnie od programu głównego. Dlaczego "prawie niezależnie" ? "Prawie" w tym przypadku pozwala naszej aplikacji na zarządzanie wątkami, tzn. można je uruchamiać, pauzować, zatrzymywać, zamykać, itp...
Wiemy już, że o wątkach należy myśleć jako o osobnych programach działających "w tle" naszej aplikacji. Dzięki ich niezależności, czyli działaniu, można rzec, równoległym do programu głównego, można je wykorzystywać do wielu zadań, które wymagają długotrwałych obliczeń. W praktyce wygląda to tak, iż program główny ma wolne od pracy (może przetwarzać komunikaty systemowe i komunikować się z użytkownikiem), a w tle wykonywana jest ciężka praca ;)
Jak tworzyć i obsługiwać wątki?
Wątek deklarujemy w naszym programie jako osobną klasę dziedziczącą (czyli posiadającą te same procedury i funkcje) z klasyTThread. Zapisujemy ją przed słówkiemvar(globalnym)
.............
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
TWatek = class(TThread)
public
procedure Execute; override;
end;
var
.............
Jak widzicie, za deklaracje klasy wątku odpowiada zapis:TWatek = class(TThread)
public
procedure Execute; override;
end;
Jedyna zadeklarowana procedura w tej klasie toExecute. Jest ona oznaczona klauzurąoverride. To właśnie słynne przedefiniowanie metody, czyli mówiąc prościej nadpisanie starej procedury, którą najwidoczniej odziedziczyliśmy z klasyTThread, nową procedurą o takiej samej nazwie. To tak jakbyście do katalogu z plikiem daniel.txt ;) przenieśli plik o tej samej nazwie ("daniel.txt"). W takiej sytuacji system zapyta się nas czy zastąpić stary plik nowym, ponieważ w katalogu nie mogą istnieć pliki o takich samych nazwach. Jeśli klikniecie na TAK, to właśnie przedefiniowaliście plik, jeśli natomiast klikniecie NIE - system nie zrobi nic. Tak samo jest z procedurami. W danej klasie nie mogą istnieć procedury o takich samych nazwach i tych samych parametrach*. W naszym wypadku musimy nadpisać proceduręExecute, gdyż w klasieTThread, z której dziedziczymy wszystkie procedury i funkcje istnieje już procedura o tej samej nazwie.
procedure WyswietlNazwePanstwa(panstwo : integer); overload;
procedure WyswietlNazwePanstwa(panstwo : string); overload;
To, która z metod zostanie wywołana (przecież obie mają takie same nazwy) zależy od typu zmiennej jaką przekażemy do procedury jako parametr. Jeśli jako parametr przekażemy łańcuch tekstowy (string), zostanie wywołana druga procedura, jeśli zaś liczbę (integer), zostanie wykonana pierwsza procedura. OK, to tak na marginesie.
Po zdefiniowaniu klasy, musimy ją zadeklarować (cicho, nie ja wymyślałem te nazwy ;) w sekcjivar(globalnej). Zanim to jednak zrobimy, dopiszcie przed globalną sekcjąvar, słówkoconst(stałe), a pod nim:
const Tekst = 'To jest tekst, który ma się przesuwać...';
Teraz dopiero w sekcjivar(zmienne) dopiszcie potrzebne do naszego programu zmienne:
var
Watek: TWatek;
x, y : integer;
Zdefiniujmy (czyli napiszmy ;) proceduręExecutew niej zadeklarowaną. Przystępujemy do tego pod sekcjąimplementation. Piszemy:
procedure TWatek.Execute;
begin
While not Watek.Terminated Do
Begin
x := 0;
y := 0;
Repeat
Sleep(10);
If Form1.Canvas.LockCount <> 0 Then
Continue;
Inc(x,1);
Form1.Canvas.Lock;
Form1.Canvas.Refresh;
Form1.Canvas.TextOut(x,y,tekst);
Form1.Canvas.UnLock;
Until (x>Form1.Width);
end;
end;
Pierwsza linijka to pętla typuwhile, która kończy się wtedy, gdy zakończymy nasz wątekWatek.Terminated. Można tą linijkę przetłumaczyć na polski następująco: "dopóki wątek nie jest zakończony rób"
Druga linijka: słówko begin ;)
Trzecia linijka: Wyzerowanie zmiennej X.
Czwarta linijka: Wyzerowanie zmiennej Y.
Piąta linijka: Pętla typurepeat, która jest odpowiedzialna za przesuwanie napisu.
Szósta linijka: Instrukcja oznaczająca, zatrzymanie wykonywania programu (w tym wypadku wątku) na 10 milisekund.
Siódma linijka: To warunek, sprawdzający, czy płótno nie jest zablokowane (gdy płótno jest zablokowane właściwość LockCount przyjmuje wartość różną od zera). W tym wypadku, jeśli płótno jest zablokowane...
Ósma linijka: ...następuje przeskok do linijki piątej.
Dziewiąta linijka: Powiększenie wartości zmiennej x o 1. (Inc(x,1)robi to samo cox := x + 1;tylko [podobno] szybciej ).
Dziesiąta linka: Zablokowanie dostępu do płótna (canvas) formularza. Ta instrukcja jest bardzo ważna i często zapominana, co prowadzi do trudnych do wykrycia błędów. O tym dlaczego należy blokować płótno czytaj
poniżej.
Jedenasta linijka: Odświeżenie płótna formularza przed rysowaniem. Ta czynność czyści płótno formularza.
Dwunasta linijka: Narysowanie tekstu na pozycji (X,Y) na płótnie.
Trzynasta linijka: Z racji, że nie będziemy już nic robić na płótnie, należy je odblokować.
Czternasta linijka: To warunek zakończenia pętlirepeat. Jak widzimy, pętla kończy się gdy wartość zmiennej X przekroczy szerokość formularza. W praktyce oznacza to, że pętla jest zakańczana gdy przesuwany tekst wyjedzie poza szerokość formularza. Wtedy kończy się pętlarepeat, a z racji, że całość umieszczona jest jeszcze w pętli typuwhile, następuje przejście do pierwszej linijki ;) i czynności są powtarzane tak aż do zakończenia działania wątku.
Pozostało jeszcze tylko napisać dwie instrukcje, które posłużą do zastartowania i zakończenia wątku. Napiszemy je w sekcjachinitialization(wykonywanej przy uruchamianiu programu) orazfinalization(wykonywanej przy zakańczaniu programu). Dopiszmy te sekcje na samym końcu, ale jeszcze przed słówkiemend.(słówkoEND.z kropką na końcu).
initialization
Watek := TWatek.Create(False);
W tej sekcji tworzymy klasę wątku, uruchamiając go tym samym. Parametr w nawiasie oznacza iż nie chcemy ręcznie uruchamiać wątku, ale automatycznie, zaraz po utworzeniu.
Gdybyśmy zmienili parametr naTrue, wówczas wątek nie zastartowałby automatycznie i należałoby go wznowić (włączyć) instrukcją:
Watek.Resume;
Pracę wątku można na pewien czas wstrzymać instrukcją:
Watek.Suspend;
aż do ponownego wznowienia instrukcją Resume.
finalization
Watek.Terminate;
W tej sekcji (przy zakańczaniu programu) zakańczamy też wątek, do czego służy instrukcjaTerminate.
Po co blokować płótno formularza (canvas) ?
Bardzo często nie zablokowanie płótna prowadzi do poważnych błędów, trudnych do wytropienia. Aby tego uniknąć, zawsze pamiętajmy o zablokowaniu płótna jeszcze przed jakimikolwiek operacjami na nim. Aby zrozumieć, dlaczego to takie ważne, wyobraźcie sobie sytuacje, w której w waszym programie działają dwa wątki. Tak się złożyło, że oba, jednocześnie, próbują narysować coś naCanvasie. Co będzie? Błąd. Błąd, który jest zmorą programistów ;) Stosowanie instrukcjiLockiUnLockpomaga w zlikwidowaniu tych błędów. Wówczas, drugi wątek próbujący uzyskać dostęp do płótna, albo poczeka aż płótno się zwolni, albo nie wykona instrukcji (zależnie od programu). Ważną właściwością która pozwala sprawdzić czy płótno jest zablokowane jestLockCount. Pozwala postawić warunek (jeśli płótno jest zablokowane, to...).
ProcedurLockiUnLocknależy używać we wszystkich przypadkach, a w szczególności wtedy, gdy mamy do czynienia z wątkami.
Możecie uruchomić program. Powinien działać prawidłowo. Na pierwszy rzut oka nie widać, że mamy do czynienia z wątkiem. Można to jednak sprawdzić przesuwając okno. W "normalnym" przypadku, bez użycia wątkowości program nie odebrałby komunikatu o chęci przesunięcia okna i klapa. Znów można byłoby odczuć, że jest to zawieszona aplikacja.
Ponadto, teraz, program główny może działać i wykonywać równolegle inną czynność, np. obsługiwać naciśnięcie przycisku bez przerywania innych procedur.
Priorytety wątków
Wątkom można również nadawać priorytety, w zależności od tego jak ważna jest wykonywana przezeń czynność. Aby nadać naszemu wątkowi odpowiedni priorytet należałoby dopisać w sekcjiinitializationtaką instrukcje:
initialization
Watek := TWatek.Create(False);
Watek.Priority := tpIdle;
W tym wypadku, wątek uzyskał priorytet jałowy. Od tej pory jego kod wykonywany będzie jedynie wtedy, gdy procesor nie będzie miał lepszego zajęcia. Możliwe są jeszcze inne priorytety, oto one:
Priorytet | Opis |
---|---|
tpIdle | Tzw. priorytet jałowy. Jest on wykonywany wtedy, gdy procesor nie ma lepszego zajęcia, a system operacyjny nie trudzi się przerywaniem innych zadań dla wykonania wątku oznaczonego tym priorytetem. |
tpLowest | Priorytet bardzo niski. Procesor nie poświęca za dużo uwagi na wykonanie tego zadania. |
tpLower | Priorytet niski - poniżej normalnego. |
tpNormal | Priorytet normalny. Priorytet normalny uzyskuje każdy wątek, dla któremu nie określono priorytetu. |
tpHigher | Priorytet wysoki - powyżej normalnego. |
tpHighest | Priorytet bardzo wysoki. Procesor poświęca dużo uwagi aby porządnie wykonać swoją pracę. Po użyciu takiego priorytetu, cały system może znacznie zwolnić. |
tpTimeCritical | Najwyższy możliwy priorytet. Przeznaczony jest tylko dla zadań specjalnych, gdzie potrzebna jest natychmiastowa reakcja na bodziec ;) Priorytet czasu rzeczywistego (bo tak się nazywa) oznacza, że gdy wątek będzie chciał wykonać jakąś operację, wszelkie inne zadania zostaną przerwane. Przy niektórych programach może to doprowadzić do zawieszenia działania systemu operacyjnego na czas wykonywania zadania. |
Tak naprawdę, jeśli pracujesz w Windows XP, priorytety mają tu mniejsze znaczenie niż w Windows 9x . W XP rzadko spotyka się aby jakaś aplikacja zawiesiła cały system operacyjny tylko dlatego, że żądała zbyt wiele czasu procesora. W systemie XP zastosowane są bardzo dobre mechanizmy zarządzające czasem procesora. Sytuacja z Windows 9x raczej nie powinna się tu powtórzyć.
Priorytet aplikacji
Jeśli chodzi o priorytety, to nie tylko w wątkach możemy je stosować. Również naszej aplikacji głównej możemy przypisać jakiś priorytet. Aby to zrobić piszemy:
SetPriorityClass(Application.Handle, IDLE_PRIORITY_CLASS);
możliwe są jeszcze następujące priorytety:
Priorytet | Opis |
---|---|
IDLE_PRIORITY_CLASS | Tzw. priorytet jałowy. Jest on wykonywany wtedy, gdy procesor nie ma lepszego zajęcia, a system operacyjny nie trudzi się przerywaniem innych zadań dla wykonania wątku oznaczonego tym priorytetem. |
NORMAL_PRIORITY_CLASS | Priorytet normalny. Priorytet normalny uzyskuje każdy wątek, dla któremu nie określono priorytetu. |
HIGH_PRIORITY_CLASS | Priorytet wysoki - powyżej normalnego. |
REALTIME_PRIORITY_CLASS | Najwyższy możliwy priorytet. Przeznaczony jest tylko dla zadań specjalnych, gdzie potrzebna jest natychmiastowa reakcja na bodziec ;) Priorytet czasu rzeczywistego (bo tak się nazywa) oznacza, że gdy wątek będzie chciał wykonać jakąś operację, wszelkie inne zadania zostaną przerwane. Przy niektórych programach może to doprowadzić do zawieszenia działania systemu operacyjnego na czas wykonywania zadania. |
Procedura staje się wątkiem
Opisany przeze mnie sposób tworzenia i zarządzania wątkami jest dość skomplikowany, ale daje naprawdę duże możliwości i ułatwia bardziej złożone czynności (np. synchronizacja wątków).
Jeśli jednak nie potrzebujemy aż tak dużych możliwości możemy stworzyć nowy wątek z... pojedynczej procedury. Tak! Jest to normalny i niezwykle przydatny sposób tworzenia wątków w Windows. Wystarczy jedynie napisać nową procedurę gdzieś w module i utworzyć z niej samodzielny wątek za pomocą funkcji CreateThread().
Utwórzmy więc nowy projekt w Delphi i połóżmy na formie przycisk. Kliknijmy na niego dwukrotnie - pojawi się edytor kodu, oczekujący na uzupełnienie procedury obsługi zdarzenia OnClick. Procedurę tę uzupełniamy następująco:
procedure TForm1.Button1Click(Sender: TObject);
var
uchwyt_watku : THandle;
id_watku : cardinal;
begin
uchwyt_watku := CreateThread(nil,0,@Watek,nil,0,id_watku);
if uchwyt_watku = 0 Then
MessageBox(Handle,PChar('Wystąpił błąd przy tworzeniu nowego wątku o ID='+IntToStr(id_watku)),'Błąd',MB_OK+MB_ICONERROR);
end;
W pierwszej linijce następuje wywołanie funkcji CreateThread(). Jej deklaracja przedstawia się następująco:function CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId);
A oto opis poszczególnych parametrów:
lpThreadAttributes- wskaźnik do rekordu zawierającego dane o zabezpieczeniach [domyślnie: nil]
dwStackSize- rozmiar stosu dla tworzonego wątku (jeśli wpiszemy 0 to przyjmie on wartość domyślną tj. 1MB) [domyślnie: 0]
lpStartAddress- wskaźnik do procedury będącej wątkiem [przykład @MojaProceduraWatku]
lpParameter- wskaźnik do argumentu dla procedury głównej wątku [przykład: @parametr]dwCreationFlags- tzw. flagi - mogą przyjąć następujące postaci: CREATE_SUSPENDED, STACK_SIZE_PARAM_IS_A_RESERVATION (nie działa w Win9x,WinMe,Win2000 oraz WinNT) [domyślnie: 0]
lpThreadId- wskaźnik do zmiennej do której zostanie zwrócony identyfikator wątku [domyślnie: zmienna Cardinal]
Funkcja CreateThread, ze wszystkimi parametrami jakie przyjmuje wygląda przerażająco - nam jednak będą potrzebne jedynie dwa z nich i wynik zwracany przez funkcję będący uchwytem nowo-utworzonego wątku. Dzięki uchwytowi, będziemy mogli kontrolować nasz wątek, np. uśpić go na jakiś czas, wznowić, zakończyń czy nawet zmienić priorytet.
Ważną informacją może okazać się fakt, iż, jeśli funkcja zwróci uchwyt o wartosci 0 - to wystąpił błąd, którego numer możemy zidentyfikować wywołując funkcję GetLastError().
A nam pozostało jedynie wpisać gdzieś przed naszą procedurą, definicję głównej procedury wątku. Wygląda ona następująco (oczywiście jest to przykład):
procedure Watek();
var
i : byte;
begin
i := 0;
while not Application.Terminated Do
begin
Inc(i);
Form1.Canvas.Brush.Color := RGB(i,i,i);
Form1.Canvas.Rectangle(0,0,400,200);
Sleep(10);
end;
end;
Jak pewnie zauważyliście - użyłem w tej procedurze funkcji Sleep(), która odpowiada za zawieszenie działania programu na określony w milisekundach czas. Gdybyśmy wywołali taką procedurę z poziomu programu - zawiesiłaby ona działanie całej aplikacji. My jednak korzystamy z wątków działających prawie niezależnie od naszej aplikacji i to ich działanie zawieszamy, co nie wpływa na funkcjonowanie naszej aplikacji głównej.
Zakończenie
Dzięki wszystkim za uwagę! Mam nadzieję, że ktoś skorzystał (lub skorzysta) z informacji tu zawartych. Tutaj macie źródełko pierwszego pisanego programu, natomiast tutaj jest źródło przykładu z funkcją CreateThread. Jeśli macie jakieś pytania, kierujecie je na poniższy adres.
Listy czytelników
Pytanie: List od Szymona - "Pytanie na temat wątków w Delphi" (dotyczy synchronizacji wątków)
Witam
Przeglądając informację na temat programów wielowątkowych pisanych w delphi, natrafiłem na Twoją stronę. Mam pytanie odnośnie synchronizacji wątków, poprzez funkcję "synchronize(watek_przykładowy)". Otóż gdy przerobie Twój program z płynącym napisem, tak , że wszystkie instrukcje skopiuje do oddzielnej procedury i wywołam ją właśnie za pomocą synchronize to program zachowuje się tak jakby nie było w nim wątków - tzn nie odpowiada. Możesz mi wyjaśnić gdzie robę błąd?
Pozdrawiam
Szymon
Ps.Program po przeróbkach wygląda tak:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
TWatek = class(TThread)
public
procedure Execute; override;
procedure watek1;
end;
const
Tekst = 'To jest tekst, który ma się przesuwać...';
var
Form1: TForm1;
Watek: TWatek;
x, y : integer;
implementation
{$R *.dfm}
procedure Twatek.watek1;
begin
x := 0;
y := 0;
Repeat
Sleep(10);
If Form1.Canvas.LockCount <> 0 Then
Continue;
Inc(x,1);
Form1.Canvas.Lock;
Form1.Canvas.Refresh;
Form1.Canvas.TextOut(x,y,tekst);
Form1.Canvas.Unlock;
Until (x>Form1.Width);
end;
procedure TWatek.Execute;
begin
While not Watek.Terminated Do
synchronize(watek1);
end;
initialization
Watek := TWatek.Create(False);
Watek.Priority := tpIdle;
finalization
Watek.Terminate;
end.
Odpowiedź: List od Szymona - "Pytanie na temat wątków w Delphi" (dotyczy synchronizacji wątków)
Cześć, odnośnie Twojego pytania [...] Natomiast odpowiadając na Twoje pytanie - błąd masz przy użyciu synchronize. Aby zrozumieć dlaczego tak się dzieje (dlaczego Twoja aplikacja się zawiesza), musisz wiedzieć jak działa metoda synchronize. Otóż metoda którą podajesz jako parametr do synchronize będzie wykonywana w głównej pętli programu - czyli w głównej pętli VCL. Czyli wątek jest jakby zatrzymywany i część kodu dla którego wywołujesz synchronize() (czyli ta Twoja metoda) będzie wykonywana w kodzie głównej aplikacji. A z racji, że w Twojej synchronizowanej procedurze masz pętle - to będzie to samo jakbyś wstawił pętle do kodu głównej aplikacji - program zawiesi się. Dlatego do metody synchronize pakuje się tak mało kodu jak się tylko da - nie więcej. W tym wypadkiem minimum kodu dla Ciebie będą cztery linijki wykonujące operację na Formularzu aplikacji głównej (a więc powinny być one synchronizowane).
Form1.Canvas.Lock;
Form1.Canvas.Refresh;
Form1.Canvas.TextOut(x,y,tekst);
Form1.Canvas.Unlock;
Natomiast pętlę zostawiamy by działała w wątku. I teraz tłumaczę jak to działa (prawidłowy kod który Ci podsyłam). Wątek rozpoczyna się, wchodzimy w pętlę "while" i przechodzimy do procedury Watek1. W procedurze Watek1 wchodzimy w pętlę "repeat", cośtam robimy i następuję synchronizowane wywołanie procedury WypiszTekst(). Teraz dzieje się rzecz następująca - wątek zostaje zapauzowany, a wywołanie procedury WypiszTekst() zostaje dodane do specjalnej kolejki synchronizowanych wywołań VCL. W głównej pętli programu (w pętli okienkowej) obsługiwane są komunikaty (np. kliknięcie myszką, naciśnięcie klawisza - później przekazywane do procedur MouseClick itp...), ale również sprawdzana jest właśnie zawartość kolejki synchronizowanych wywołań. Jeśli jakaś metoda została umieszczona w kolejce - zostanie ona wywołana właśnie teraz, w pętli głównej aplikacji. Po zakończeniu jej wykonywania wątek jest wznawiany i wykonywane są instrukcje po wywołaniu synchronize() i proces się powtarza.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
TWatek = class(TThread)
public
procedure Execute; override;
procedure watek1;
procedure WypiszTekst();
end;
const
Tekst = 'To jest tekst, który ma się przesuwać...';
var
Form1: TForm1;
Watek: TWatek;
x, y : integer;
implementation
{$R *.dfm}
procedure TWatek.WypiszTekst();
begin
Form1.Canvas.Lock;
Form1.Canvas.Refresh;
Form1.Canvas.TextOut(x,y,tekst);
Form1.Canvas.Unlock;
end;
procedure Twatek.watek1;
begin
x := 0;
y := 0;
Repeat
Sleep(10);
If Form1.Canvas.LockCount <> 0 Then
Continue;
Inc(x,1);
Synchronize(WypiszTekst);
Until (x>Form1.Width);
end;
procedure TWatek.Execute;
begin
While not Watek.Terminated Do
watek1;
end;
initialization
Watek := TWatek.Create(False);
Watek.Priority := tpIdle;
finalization
Watek.Terminate;
end.
Pozdrawiam!
Podobne artykuly:
- KillAd
- Delphi - Budowa modułu
- Delphi - Piszemy własny odtwarzacz multimedialny
- Delphi - Piszemy prosty edytor tekstu
- Delphi - Jak pisać?
- Delphi - Pobieranie plików z Internetu
- Delphi - Potęga możliwości ShellExecute()
Skomentuj
Komentarze czytelników
-
- darkus
- Thu, 30 April 2015, 12:24
- Bardzo ciekawie i zrozumiale napisane. Gratuluję pomysłu oraz umiejętności!
-
- tj_gumis
- Sun, 29 May 2011, 14:35
- Naprawde rewelacyjnie piszesz.
Lekka reka i bardzo przejrzyscie.
Bardzo Ci dziekuje za Twoj wysilek
Odp: Ależ to jest miłe. Dziękuję.
-
- jacyk
- Thu, 9 September 2010, 23:18
- Witam
Chciałbym żebyś mi wytłumaczył (jeśli masz ochotę) zasadę działania wątków bo nie mogę ich zrozumieć do końca :( dla mnie to część programu która działa na zasadzie powtarzania aż do uzyskania określonej wartości więc w czym to się różni od normalnego kodu?
Odp: Wątek działa pod władzą programu głównego. Jednak jest od niego
niezależny. Najprościej mówiąc, jeśli program główny wykonuje
jakieś operacje, to bez użycia wątków nie możemy wykonywać
w nim równolegle innych operacji. Przykładowo, jeśli mamy w
głównym programie linię która jest odpowiedzialna za ściągnięcie
z sieci jakiegoś pliku:
// Aby zadziałało, dodaj słówko UrlMon do listy uses
ShowMessage('Rozpoczynam pobieranie pliku');
UrlDownloadToFile(nil, http://www.lukas-home-page.ovh.org/artykuly/delphi-watki.php', 'C:plik.html', 0, nil);
ShowMessage('To jest wiadomosc, ktora wyswietli sie dopiero po wykonaniu pobierania - nie wczesniej');
to kod który jest po linii zawierającej wywołanie funkcji
UrlDownloadToFile() zostanie wykonany dopiero gdy funkcja
ta zakończy swoje działanie. Przypuśćmy, że w Twoim programie
te instrukcje pobierają najnowsze aktualizacje do Twojego
programu. Więc jeśli piszesz np. odtwarzacz animacji -
animacja zostanie wstrzymana do momentu w którym takie
pobieranie się zakończy. Chcąc uniknąć efektu wstrzymywania
aplikacji na ten czas, należy umieścić procedurę sprawdzającą
i pobierającą aktualizacje w osobnym wątku. Dzięki temu
będzie działała ona niezależnie od programu głównego
(wątek można traktować jako mniejszy program poboczny mający
dostęp do zmiennych programu głównego i działający w jego
wnętrzu). Uruchomienie nowego wątku jest jak uruchomienie
nowego programu, który jest niezależny od Twojego
programu (aczkolwiek zakończenie głównego programu,
zakańcza też wątki), tyle, że jest to znacznie szybsze,
wątek można kontrolować, no i wątek ma dostęp do zmiennych
globalnych w Twoim programie.
-
- eSeLU
- Sun, 27 June 2010, 0:41
- Wspaniały artykuł ;) Łukasz Twoja stronka jest naprawdę super nie raz z niej korzystam żeby odświeżyć wiedze ;) oby tak dalej :)
Odp: No to fajnie. Miło wiedzieć, że komuś się to przydaje ;-) Pozdrawiam serdecznie!
-
- Ami6669
- Tue, 11 August 2009, 13:32
- No cóż mogę powiedzieć Lukas...
Jedyna wada tego artykułu to... Mało komentów :D
Muszę przyznać, że po poznaniu "tajników" wątków mogę teraz w pełni pracować nad swoimi Projektami! ;] Tego brakowąło mi do pełnej wiedzy...
Art napisany bardzo fajnie, każda osoba wiedząca co nie co o programowaniu zrozumie go z łatością..
Jest lekki, treściwy, ale i ma dużo treści - żadnych zbędności! ;]
Ogólnie podziwiam Ciebie... Na programowaniu też się znam, ale nieumiem tak tłumaczyć różnych rzeczy :P Masz i talent do tego ;]
Pozdrawiam Cię serdecznie i życzę kolejnych udanych artów!! Wtedy ludzie tacy jak ja i moi znajomi będą wielokrotnie powracać na Twoją stronę... :))
-
- bartlomij
- Mon, 5 May 2008, 15:27
- nie mozliwe?? :D