Sign in Join
Wiki copytemporary

Example of multi-threaded application: array of threads/pl: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11Przykład aplikacji wielowątkowej: tablica wątków
22
3-Tutaj chcę pokazać przykład, jak utworzyć wiele wątków i poczekać, aż zakończą swoje zadania (nie potrzebuję żadnej synchronizacji). Piszę ten tutorial, ponieważ po przeczytaniu [[Multithreaded Application Tutorial/pl|Samouczek aplikacji wielowątkowej] nie było dla mnie oczywiste napisanie takiego programu. Pisałem aplikację dla systemu macOS, ale powstały kod powinien działać na każdym systemie.
3+Tutaj chcę pokazać przykład, jak utworzyć wiele wątków i poczekać, aż zakończą swoje zadania (nie potrzebuję żadnej synchronizacji). Piszę ten tutorial, ponieważ po przeczytaniu \[\[Multithreaded Application Tutorial/pl\|Samouczek aplikacji wielowątkowej\] nie było dla mnie oczywiste napisanie takiego programu. Pisałem aplikację dla systemu macOS, ale powstały kod powinien działać na każdym systemie.
44
5-Załóżmy, że mamy następującą pętlę:
6-```
5+Załóżmy, że mamy następującą pętlę:
6+
7+``` pascal
78...
8- for i:=1 to n do begin
9- power(i,0.5)
10- end;
11- ...
9+for i:=1 to n do begin
10+ power(i,0.5)
11+end;
12+...
1213```
1314
14-Ta pętla wykonuje n razy potęgowanie iteratora ze stopniem 0,5 (co odpowiada pierwiastkowaniu 2 stopnia). Użyjmy wątków, aby osiągnąć to samo zadanie równolegle.
15-
16-## Contents
17-
18- * 1 Czy potrzebujemy wielu wątków?
19- * 2 Zarządzanie pamięcią
20- * 3 1\. Wykryj liczbę dostępnych rdzeni.
21- * 4 2\. Utwórz niestandardową klasę wątków.
22- * 5 3\. Tworzenie głównego programu.
23- * 6 Wyniki.
15+Ta pętla wykonuje n razy potęgowanie iteratora ze stopniem 0,5 (co odpowiada pierwiastkowaniu 2 stopnia). Użyjmy wątków, aby osiągnąć to samo zadanie równolegle.
2416
2517## Czy potrzebujemy wielu wątków?
2618
27-W nowoczesnych komputerach z wielordzeniowymi procesorowymi, użycie wielu wątków może radykalnie zwiększyć wydajność. Jednak nowoczesne komputery są bardzo szybkie, a kod wielowątkowy jest często trudniejszy do debugowania i konserwacji niż kod jednowątkowy. Należy rozważyć, czy zaoszczędzony czas przetwarzania uzasadnia złożoność programowania wielowątkowego i czy algorytm dobrze nadaje się do obliczeń równoległych. Na koniec należy zauważyć, że niektóre algorytmy, które mogą czerpać korzyści z obliczeń równoległych, mogą skorzystać na wykorzystaniu wątków procesora (jak pokazano tutaj), podczas gdy inne mogą być bardziej dostosowane do procesora graficznego (gdzie narzędzia takie jak OpenCL mogą być bardziej optymalne).
19+W nowoczesnych komputerach z wielordzeniowymi procesorowymi, użycie wielu wątków może radykalnie zwiększyć wydajność. Jednak nowoczesne komputery są bardzo szybkie, a kod wielowątkowy jest często trudniejszy do debugowania i konserwacji niż kod jednowątkowy. Należy rozważyć, czy zaoszczędzony czas przetwarzania uzasadnia złożoność programowania wielowątkowego i czy algorytm dobrze nadaje się do obliczeń równoległych. Na koniec należy zauważyć, że niektóre algorytmy, które mogą czerpać korzyści z obliczeń równoległych, mogą skorzystać na wykorzystaniu wątków procesora (jak pokazano tutaj), podczas gdy inne mogą być bardziej dostosowane do procesora graficznego (gdzie narzędzia takie jak OpenCL mogą być bardziej optymalne).
2820
2921## Zarządzanie pamięcią
3022
31-Ponieważ wiele wątków będzie działać jednocześnie, należy upewnić się, że nie występują w nich problemy z rywalizacją o pamięć. Jeśli wiele wątków zapisuje dane w tych samych lokalizacjach w pamięci, wystąpią problemy z rywalizacją o pamięć. Niektóre algorytmy nie nadają się do wielowątkowości, ponieważ każde obliczenie zależy od wcześniejszych wyników. Z drugiej strony wielowątkowość działa bardzo efektywnie w przypadku problemów, w których obliczenia można wykonywać niezależnie i równolegle. W tym przykładzie rozwiążemy problem, który jest całkowicie niezależny i dlatego łatwy do zrealizowania wieloma wątkami. Zaawansowane algorytmy będą musiały korzystać z funkcji blokowania pamięci, aby uniknąć rywalizacji.
23+Ponieważ wiele wątków będzie działać jednocześnie, należy upewnić się, że nie występują w nich problemy z rywalizacją o pamięć. Jeśli wiele wątków zapisuje dane w tych samych lokalizacjach w pamięci, wystąpią problemy z rywalizacją o pamięć. Niektóre algorytmy nie nadają się do wielowątkowości, ponieważ każde obliczenie zależy od wcześniejszych wyników. Z drugiej strony wielowątkowość działa bardzo efektywnie w przypadku problemów, w których obliczenia można wykonywać niezależnie i równolegle. W tym przykładzie rozwiążemy problem, który jest całkowicie niezależny i dlatego łatwy do zrealizowania wieloma wątkami. Zaawansowane algorytmy będą musiały korzystać z funkcji blokowania pamięci, aby uniknąć rywalizacji.
3224
33-W naszym przykładzie każdy wątek będzie zapisywał w różnych lokalizacjach pamięci. W szczególności utworzymy tablicę 1..n i obliczymy wartość power(i,0.5) (potegi), gdzie i należy do zakresu 1..n. Każdy wątek otrzyma niezależną część zakresu do obliczenia. Rozważ n=1000. Jeśli użyjemy jednego wątku, będzie on miał do wykonania zadanie z całego zakresu 1..1000, natomiast jeśli zastosujemy dwa wątki, jeden zajmie się zakresem 1..500, a drugi 501..1000. W ten sposób wątki będą pracować nad wypełnieniem różnych części naszej tablicy pamięci.
25+W naszym przykładzie każdy wątek będzie zapisywał w różnych lokalizacjach pamięci. W szczególności utworzymy tablicę 1..n i obliczymy wartość power(i,0.5) (potegi), gdzie i należy do zakresu 1..n. Każdy wątek otrzyma niezależną część zakresu do obliczenia. Rozważ n=1000. Jeśli użyjemy jednego wątku, będzie on miał do wykonania zadanie z całego zakresu 1..1000, natomiast jeśli zastosujemy dwa wątki, jeden zajmie się zakresem 1..500, a drugi 501..1000. W ten sposób wątki będą pracować nad wypełnieniem różnych części naszej tablicy pamięci.
3426
35-## 1\. Wykryj liczbę dostępnych rdzeni.
27+## 1. Wykryj liczbę dostępnych rdzeni.
3628
37-Komputer posiadający tylko jeden rdzeń nie odniesie korzyści z obsługi wątków, podczas gdy komputer z czterema fizycznymi rdzeniami, każdy z funkcją hyperthreading (będący w stanie wykonywać dwa zadania jednocześnie), będzie w stanie przetwarzać do ośmiu zadań jednocześnie. Niżej pokazany moduł „cpucount” podaje liczbę dostępnych rdzeni. Możesz użyć tej opcji, aby określić, z ilu wątków powinien korzystać Twój program na danym komputerze. W przypadku komputerów z czterema lub więcej rdzeniami wiele osób chce uruchamiać wątki n-1 (gdzie n to liczba rdzeni), rezerwując jeden rdzeń na potrzeby interfejsu graficznego i innych zadań, ponieważ różnica w wydajności między wątkami n i n-1 nie będzie istotna z tak dużą liczbą rdzeni.
29+Komputer posiadający tylko jeden rdzeń nie odniesie korzyści z obsługi wątków, podczas gdy komputer z czterema fizycznymi rdzeniami, każdy z funkcją hyperthreading (będący w stanie wykonywać dwa zadania jednocześnie), będzie w stanie przetwarzać do ośmiu zadań jednocześnie. Niżej pokazany moduł „cpucount” podaje liczbę dostępnych rdzeni. Możesz użyć tej opcji, aby określić, z ilu wątków powinien korzystać Twój program na danym komputerze. W przypadku komputerów z czterema lub więcej rdzeniami wiele osób chce uruchamiać wątki n-1 (gdzie n to liczba rdzeni), rezerwując jeden rdzeń na potrzeby interfejsu graficznego i innych zadań, ponieważ różnica w wydajności między wątkami n i n-1 nie będzie istotna z tak dużą liczbą rdzeni.
3830
39-
31+> **Note:** Definicja funkcji użyta poniżej dla FPsysctl() dotyczy FPC v3.0.4 - Dla FPC v3.2.0 i v3.3.1 (trunk) pierwszym argumentem nie jest już *pchar*, ale *pcint*. Dostosuj odpowiednio przykładowy kod.
4032
41-![Light bulb](https://upload.wikimedia.org/wikipedia/commons/d/d8/Nuvola_apps_ktip.png) **Uwaga:** Definicja funkcji użyta poniżej dla FPsysctl() dotyczy FPC v3.0.4 - Dla FPC v3.2.0 i v3.3.1 (trunk) pierwszym argumentem nie jest już _pchar_ , ale _pcint_. Dostosuj odpowiednio przykładowy kod.
42-```
33+``` pascal
4334unit cpucount;
44-
45- {$mode objfpc}{$H+}
46-
47- interface
48- //zwraca liczbę rdzeni: komputer z dwoma rdzeniami hiperwątkowymi zgłosi 4
49- function GetLogicalCpuCount: Integer;
50-
51- implementation
52-
53- {$IF defined(windows)}
54- uses windows;
55- {$endif}
56-
57- {$IF defined(darwin)}
58- uses ctypes, sysctl;
59- {$endif}
60-
61- {$IFDEF Linux}
62- uses ctypes;
63-
64- const _SC_NPROCESSORS_ONLN = 83;
65- function sysconf(i: cint): clong; cdecl; external name 'sysconf';
66- {$ENDIF}
67-
68-
69- function GetLogicalCpuCount: integer;
70- // zwraca dobrą domyślną liczbę wątków w tym systemie
71- {$IF defined(windows)}
72- //zwraca całkowitą liczbę procesorów dostępnych w systemie, w tym logiczne procesory hiperwątkowe
73- var
74- i: Integer;
75- ProcessAffinityMask, SystemAffinityMask: DWORD_PTR;
76- Mask: DWORD;
77- SystemInfo: SYSTEM_INFO;
78- begin
79- if GetProcessAffinityMask(GetCurrentProcess, ProcessAffinityMask, SystemAffinityMask)
80- then begin
81- Result := 0;
82- for i := 0 to 31 do begin
83- Mask := DWord(1) shl i;
84- if (ProcessAffinityMask and Mask)<>0 then
85- inc(Result);
86- end;
87- end else begin
88- //nie można uzyskać maski koligacji, więc po prostu raportujemy całkowitą liczbę procesorów
89- GetSystemInfo(SystemInfo);
90- Result := SystemInfo.dwNumberOfProcessors;
91- end;
92- end;
93- {$ELSEIF defined(UNTESTEDsolaris)}
94- begin
95- t = sysconf(_SC_NPROC_ONLN);
96- end;
97- {$ELSEIF defined(freebsd) or defined(darwin)}
98- var
99- mib: array[0..1] of cint;
100- len: cint;
101- status: integer;
102- begin
103- mib[0] := CTL_HW;
104- mib[1] := HW_NCPU;
105- len := sizeof(Result);
106- status := fpsysctl(pchar(@mib), Length(mib), @Result, @len, Nil, 0);
107- if status <> 0 then WriteLn('Error in fpsysctl()');
35+
36+{$mode objfpc}{$H+}
37+
38+interface
39+//zwraca liczbę rdzeni: komputer z dwoma rdzeniami hiperwątkowymi zgłosi 4
40+function GetLogicalCpuCount: Integer;
41+
42+implementation
43+
44+{$IF defined(windows)}
45+uses windows;
46+{$endif}
47+
48+{$IF defined(darwin)}
49+uses ctypes, sysctl;
50+{$endif}
51+
52+{$IFDEF Linux}
53+uses ctypes;
54+
55+const _SC_NPROCESSORS_ONLN = 83;
56+function sysconf(i: cint): clong; cdecl; external name 'sysconf';
57+{$ENDIF}
58+
59+function GetLogicalCpuCount: integer;
60+// zwraca dobrą domyślną liczbę wątków w tym systemie
61+{$IF defined(windows)}
62+//zwraca całkowitą liczbę procesorów dostępnych w systemie, w tym logiczne procesory hiperwątkowe
63+var
64+ i: Integer;
65+ ProcessAffinityMask, SystemAffinityMask: DWORD_PTR;
66+ Mask: DWORD;
67+ SystemInfo: SYSTEM_INFO;
68+begin
69+ if GetProcessAffinityMask(GetCurrentProcess, ProcessAffinityMask, SystemAffinityMask)
70+ then begin
71+ Result := 0;
72+ for i := 0 to 31 do begin
73+ Mask := DWord(1) shl i;
74+ if (ProcessAffinityMask and Mask)<>0 then
75+ inc(Result);
10876 end;
109- {$ELSEIF defined(linux)}
110- begin
111- Result:=sysconf(_SC_NPROCESSORS_ONLN);
112- end;
113-
114- {$ELSE}
115- begin
116- Result:=1;
117- end;
118- {$ENDIF}
119- end.
77+ end else begin
78+ //nie można uzyskać maski koligacji, więc po prostu raportujemy całkowitą liczbę procesorów
79+ GetSystemInfo(SystemInfo);
80+ Result := SystemInfo.dwNumberOfProcessors;
81+ end;
82+end;
83+{$ELSEIF defined(UNTESTEDsolaris)}
84+ begin
85+ t = sysconf(_SC_NPROC_ONLN);
86+ end;
87+{$ELSEIF defined(freebsd) or defined(darwin)}
88+var
89+ mib: array[0..1] of cint;
90+ len: cint;
91+ status: integer;
92+begin
93+ mib[0] := CTL_HW;
94+ mib[1] := HW_NCPU;
95+ len := sizeof(Result);
96+ status := fpsysctl(pchar(@mib), Length(mib), @Result, @len, Nil, 0);
97+ if status <> 0 then WriteLn('Error in fpsysctl()');
98+end;
99+{$ELSEIF defined(linux)}
100+ begin
101+ Result:=sysconf(_SC_NPROCESSORS_ONLN);
102+ end;
103+
104+{$ELSE}
105+ begin
106+ Result:=1;
107+ end;
108+{$ENDIF}
109+end.
120110```
121111
122-## 2\. Utwórz niestandardową klasę wątków.
112+## 2. Utwórz niestandardową klasę wątków.
123113
124-Do definiowania nowych wątków używam osobnego modułu. Zauważ, że ustawiam „FreeOnTerminate” na false - więc mój program będzie musiał pozbyć się każdego wątku, gdy się zakończy. Ułatwia to żonglowanie wieloma wątkami (jeśli ustawisz FreeOnTerminate na true i uruchomisz wiele bardzo szybkich zadań, możliwe jest, że wątek zostanie zwolniony, zanim program sprawdzi, czy wątek jest ukończony – a sprawdzenie nieistniejącego wątku może spowodować wyjątek). Ustawiając FreeOnTerminate na false, mogę mieć pewność, że każdy wątek zakończył się pomyślnie.
114+Do definiowania nowych wątków używam osobnego modułu. Zauważ, że ustawiam „FreeOnTerminate” na false - więc mój program będzie musiał pozbyć się każdego wątku, gdy się zakończy. Ułatwia to żonglowanie wieloma wątkami (jeśli ustawisz FreeOnTerminate na true i uruchomisz wiele bardzo szybkich zadań, możliwe jest, że wątek zostanie zwolniony, zanim program sprawdzi, czy wątek jest ukończony – a sprawdzenie nieistniejącego wątku może spowodować wyjątek). Ustawiając FreeOnTerminate na false, mogę mieć pewność, że każdy wątek zakończył się pomyślnie.
125115
126-Dobrze zachowujący się wątek w pętli powinien regularnie sprawdzać zakończenie, aby w przypadku, gdy procesy zewnętrzne (np. Destroy) chciały przerwać wykonywanie, nie musiały czekać wiecznie. Oryginalny kod tego nie robił, więc nie można było ukończyć zwolnienia pamięci (free), dopóki wszystkie obliczenia nie zostały zakończone.
116+Dobrze zachowujący się wątek w pętli powinien regularnie sprawdzać zakończenie, aby w przypadku, gdy procesy zewnętrzne (np. Destroy) chciały przerwać wykonywanie, nie musiały czekać wiecznie. Oryginalny kod tego nie robił, więc nie można było ukończyć zwolnienia pamięci (free), dopóki wszystkie obliczenia nie zostały zakończone.
127117
128-Kontrola podstawowego kodu pokazuje, że po wyjściu z polecenia „Execute” wywoływana jest funkcja OnTerminate, ale opcja Terminated nie jest ustawiona. Ponieważ w przykładzie nie używa się już funkcji waitfor, ale po prostu sprawdza się Terminated, to dodałem Terminate po zakończeniu pętli.
129-```
118+Kontrola podstawowego kodu pokazuje, że po wyjściu z polecenia „Execute” wywoływana jest funkcja OnTerminate, ale opcja Terminated nie jest ustawiona. Ponieważ w przykładzie nie używa się już funkcji waitfor, ale po prostu sprawdza się Terminated, to dodałem Terminate po zakończeniu pętli.
119+
120+``` pascal
130121unit mythreads;
131- {$mode objfpc}{$H+}
132- interface
133- uses
134- Classes, SysUtils, Math;
135- type
136- TData = array of double;
137- PData = ^TData;
138- Type
139- TMyThread = class(TThread)
140- private
141- protected
142- tPtr: PData;
143- tstart,tfinish: integer;
144- procedure Execute; override;
145- public
146- property Terminated;
147- Constructor Create(lstart, lfinish: integer; var lPtr: PData);
122+{$mode objfpc}{$H+}
123+interface
124+uses
125+ Classes, SysUtils, Math;
126+type
127+ TData = array of double;
128+ PData = ^TData;
129+ Type
130+ TMyThread = class(TThread)
131+ private
132+ protected
133+ tPtr: PData;
134+ tstart,tfinish: integer;
135+ procedure Execute; override;
136+ public
137+ property Terminated;
138+ Constructor Create(lstart, lfinish: integer; var lPtr: PData);
139+ end;
140+
141+implementation
142+
143+ constructor TMyThread.Create(lstart, lfinish: integer; var lPtr: PData);
144+ begin
145+ FreeOnTerminate := False;
146+ tstart := lstart;
147+ tfinish := lfinish;
148+ tPtr := lPtr;
149+ inherited Create(false);
150+ end;
151+ procedure TMyThread.Execute;
152+ var
153+ i: integer;
154+ begin
155+ i:= tstart;
156+ While not Terminated and (i<= tfinish) do
157+ //Dobrze zachowujący się wątek w pętli powinien regularnie sprawdzać zakończenie
158+ begin
159+ //for i := tstart to tfinish do
160+ tPtr^[i] := power(i,0.5);
161+ inc(i);
148162 end;
149-
150- implementation
151-
152- constructor TMyThread.Create(lstart, lfinish: integer; var lPtr: PData);
153- begin
154- FreeOnTerminate := False;
155- tstart := lstart;
156- tfinish := lfinish;
157- tPtr := lPtr;
158- inherited Create(false);
159- end;
160- procedure TMyThread.Execute;
161- var
162- i: integer;
163- begin
164- i:= tstart;
165- While not Terminated and (i<= tfinish) do
166- //Dobrze zachowujący się wątek w pętli powinien regularnie sprawdzać zakończenie
167- begin
168- //for i := tstart to tfinish do
169- tPtr^[i] := power(i,0.5);
170- inc(i);
171- end;
172- Terminate;
173- end;
174-
175- end.
163+ Terminate;
164+ end;
165+
166+end.
176167```
177168
178-## 3\. Tworzenie głównego programu.
169+## 3. Tworzenie głównego programu.
179170
180-Jeśli korzystasz z systemu Linux/Unix, musisz dodać moduł 'cthreads' do modułu głównego 'main', a nie do modułu z wątkami! Obecnie Lazarus domyślnie dodaje moduły warunkowo:
181-```
182-{$IFDEF UNIX}
183- cthreads,
184- {$ENDIF}
185- {$IFDEF HASAMIGA}
186- athreads,
187- {$ENDIF}
188-```
171+Jeśli korzystasz z systemu Linux/Unix, musisz dodać moduł 'cthreads' do modułu głównego 'main', a nie do modułu z wątkami! Obecnie Lazarus domyślnie dodaje moduły warunkowo:
189172
190-więc w takiej sytuacji nie musisz tego już robić. W przypadku MS Windows nie jest to potrzebne.
173+` {$IFDEF UNIX}`\
174+` cthreads,`\
175+` {$ENDIF}`\
176+` {$IFDEF HASAMIGA}`\
177+` athreads,`\
178+` {$ENDIF}`
191179
192-Należy pamiętać, że istnieją dwa sposoby sprawdzenia, czy wszystkie wątki zostały zakończone. Możesz użyć wbudowanej funkcji „waitFor” - działa to bardzo dobrze, ale na moim komputerze Mac zauważyłem, że odświeża się tylko co 100 ms. Jest to idealne rozwiązanie dla rzeczywistych programów (używamy wątków tylko w przypadku problemów obliczeniowo wolnych) i zmniejsza obciążenie wątku. Jednak w przypadku szybkich przykładowych testów porównawczych może ukryć zalety wielowątkowości (ponieważ operacje wymagają minimum 100 ms niezależnie od liczby wątków). Dlatego w tym przykładzie wykrywam status zakończenia wątku co 2 ms. Zapewnia to dokładniejszy czas analizy porównawczej.
180+więc w takiej sytuacji nie musisz tego już robić. W przypadku MS Windows nie jest to potrzebne.
193181
194-Pamiętaj, aby zwolnić każdy wątek, gdy już z nim skończysz. Ponieważ ustawiliśmy „FreeOnTerminate := False”, program musi to zrobić jawnie.
182+Należy pamiętać, że istnieją dwa sposoby sprawdzenia, czy wszystkie wątki zostały zakończone. Możesz użyć wbudowanej funkcji „waitFor” - działa to bardzo dobrze, ale na moim komputerze Mac zauważyłem, że odświeża się tylko co 100 ms. Jest to idealne rozwiązanie dla rzeczywistych programów (używamy wątków tylko w przypadku problemów obliczeniowo wolnych) i zmniejsza obciążenie wątku. Jednak w przypadku szybkich przykładowych testów porównawczych może ukryć zalety wielowątkowości (ponieważ operacje wymagają minimum 100 ms niezależnie od liczby wątków). Dlatego w tym przykładzie wykrywam status zakończenia wątku co 2 ms. Zapewnia to dokładniejszy czas analizy porównawczej.
195183
196-Wskazówka: w moim środowisku IDE Lazarusa nie mogłem debugować aplikacji wielowątkowych, jeśli nie korzystałem z 'pthreads'. Czytałem, że jeśli użyjesz 'cmem', program będzie działał szybciej, ale zdecydowanie zalecam sprawdzenie tego pod kątem konkretnego przypadku (mój program zawiesza się, gdy używam 'cmem').
197-```
184+Pamiętaj, aby zwolnić każdy wątek, gdy już z nim skończysz. Ponieważ ustawiliśmy „FreeOnTerminate := False”, program musi to zrobić jawnie.
185+
186+Wskazówka: w moim środowisku IDE Lazarusa nie mogłem debugować aplikacji wielowątkowych, jeśli nie korzystałem z 'pthreads'. Czytałem, że jeśli użyjesz 'cmem', program będzie działał szybciej, ale zdecydowanie zalecam sprawdzenie tego pod kątem konkretnego przypadku (mój program zawiesza się, gdy używam 'cmem').
187+
188+``` pascal
198189uses // cmem,pthreads,
199- cthreads, Classes, SysUtils, CustApp, MyThreads, Math, cpucount;
200-
201- procedure DoUnThreaded (nValues: integer);
202- var
203- dataArray: TData;
204- i: integer;
205- StartMS: double;
206- begin
207- if (nValues < 1) then exit;
208- StartMS:=timestamptomsecs(datetimetotimestamp(now));
209- setlength(dataArray, nValues+1);//+1 since indexed 0..n-1
210- for i:=1 to nValues do
211- dataArray[i] := power(i,0.5); ;
212- Writeln('Przetworzono szeregowo '+inttostr(nValues)+' wartości w czasie '+floattostr(timestamptomsecs(datetimetotimestamp(now))-StartMS)+'ms, wykonując '+inttostr(nValues)+'^0.5 = '+floattostr(dataArray[nValues]));
213- end;
214-
215- procedure DoThreading (nThreadsIn, nValues: integer);
216- var
217- threadArray: array of TMyThread;
218- dataArray: TData;
219- lData : PData;
220- nThreads, i,lStart,lFinish: integer;
221- StartMS: double;
222- begin
223- if (nThreadsIn < 1) or (nValues < 1) then exit;
224- nThreads := nThreadsIn;
225- if nThreads > nValues then nThreads := nValues;
226- StartMS:=timestamptomsecs(datetimetotimestamp(now));
227- setlength(threadArray,nThreads+1);//+1 do indeksowania 0..n-1
228- setlength(dataArray, nValues+1);//+1 do indeksowania 0..n-1
229- lData := @dataArray;
230- lStart := 1;
231- for i:=1 to nThreads do begin
232- if i < nThreads then
233- lFinish:=i*(nValues div nThreads)
234- else
235- lFinish:= nValues;
236- threadArray[i]:= TMyThread.Create(lStart, lFinish, lData);
237- //Writeln('Przetwarzanie '+inttostr(i)+' wątkowe zakresu '+inttostr(lStart)+'..'+inttostr(lFinish));
238- lStart := lFinish+1;
239- end;
240- //for i:=1 to nThreads do if not ThreadArray[i].Terminated then Sleep(2); //Logicznie źle, bo wprowadza opóźnienie n * 2ms
241- //for i:=1 to nThreads do threadArray[i].waitFor; //wygląda na to, że ma macOS usypia się na 100ms
242-
243- for i:=1 to nThreads do
244- While not ThreadArray[i].Terminated do Sleep(2); //nie kontynuuj, dopóki wszystkie wątki nie zostaną zakończone
245-
246- for i:=1 to nThreads do threadArray[i].Free; //Free nie zostanie ukończone do czasu zakończenia procesu wykonywania wątku.
247- Writeln('Przetworzono '+inttostr(nThreads)+' wątkowo '+inttostr(nValues)+' wartości w czasie '+floattostr(timestamptomsecs(datetimetotimestamp(now))-StartMS)+'ms, wykonując '+inttostr(nValues)+'^0.5 = '+floattostr(dataArray[nValues]));
248- end;
249-
250- begin
251- Writeln('Komputer zgłasza '+inttostr(GetLogicalCpuCount)+' rdzeni: to prawdopodobnie jest optymalna liczba wątków');
252- DoUnthreaded(10);
253- DoThreading(1,10);
254- DoThreading(2,10);
255- DoThreading(4,10);
256- DoThreading(8,10);
257- DoUnthreaded(100000000);
258- DoThreading(1,100000000);
259- DoThreading(2,100000000);
260- DoThreading(4,100000000);
261- DoThreading(8,100000000);
262- end.
190+ cthreads, Classes, SysUtils, CustApp, MyThreads, Math, cpucount;
191+
192+procedure DoUnThreaded (nValues: integer);
193+var
194+ dataArray: TData;
195+ i: integer;
196+ StartMS: double;
197+begin
198+ if (nValues < 1) then exit;
199+ StartMS:=timestamptomsecs(datetimetotimestamp(now));
200+ setlength(dataArray, nValues+1);//+1 since indexed 0..n-1
201+ for i:=1 to nValues do
202+ dataArray[i] := power(i,0.5); ;
203+ Writeln('Przetworzono szeregowo '+inttostr(nValues)+' wartości w czasie '+floattostr(timestamptomsecs(datetimetotimestamp(now))-StartMS)+'ms, wykonując '+inttostr(nValues)+'^0.5 = '+floattostr(dataArray[nValues]));
204+end;
205+
206+procedure DoThreading (nThreadsIn, nValues: integer);
207+var
208+ threadArray: array of TMyThread;
209+ dataArray: TData;
210+ lData : PData;
211+ nThreads, i,lStart,lFinish: integer;
212+ StartMS: double;
213+begin
214+ if (nThreadsIn < 1) or (nValues < 1) then exit;
215+ nThreads := nThreadsIn;
216+ if nThreads > nValues then nThreads := nValues;
217+ StartMS:=timestamptomsecs(datetimetotimestamp(now));
218+ setlength(threadArray,nThreads+1);//+1 do indeksowania 0..n-1
219+ setlength(dataArray, nValues+1);//+1 do indeksowania 0..n-1
220+ lData := @dataArray;
221+ lStart := 1;
222+ for i:=1 to nThreads do begin
223+ if i < nThreads then
224+ lFinish:=i*(nValues div nThreads)
225+ else
226+ lFinish:= nValues;
227+ threadArray[i]:= TMyThread.Create(lStart, lFinish, lData);
228+ //Writeln('Przetwarzanie '+inttostr(i)+' wątkowe zakresu '+inttostr(lStart)+'..'+inttostr(lFinish));
229+ lStart := lFinish+1;
230+ end;
231+ //for i:=1 to nThreads do if not ThreadArray[i].Terminated then Sleep(2); //Logicznie źle, bo wprowadza opóźnienie n * 2ms
232+ //for i:=1 to nThreads do threadArray[i].waitFor; //wygląda na to, że ma macOS usypia się na 100ms
233+
234+ for i:=1 to nThreads do
235+ While not ThreadArray[i].Terminated do Sleep(2); //nie kontynuuj, dopóki wszystkie wątki nie zostaną zakończone
236+
237+ for i:=1 to nThreads do threadArray[i].Free; //Free nie zostanie ukończone do czasu zakończenia procesu wykonywania wątku.
238+ Writeln('Przetworzono '+inttostr(nThreads)+' wątkowo '+inttostr(nValues)+' wartości w czasie '+floattostr(timestamptomsecs(datetimetotimestamp(now))-StartMS)+'ms, wykonując '+inttostr(nValues)+'^0.5 = '+floattostr(dataArray[nValues]));
239+end;
240+
241+begin
242+ Writeln('Komputer zgłasza '+inttostr(GetLogicalCpuCount)+' rdzeni: to prawdopodobnie jest optymalna liczba wątków');
243+ DoUnthreaded(10);
244+ DoThreading(1,10);
245+ DoThreading(2,10);
246+ DoThreading(4,10);
247+ DoThreading(8,10);
248+ DoUnthreaded(100000000);
249+ DoThreading(1,100000000);
250+ DoThreading(2,100000000);
251+ DoThreading(4,100000000);
252+ DoThreading(8,100000000);
253+end.
263254```
264255
265256## Wyniki.
266257
267-Wyniki pokazują, że występuje opóźnienie w tworzeniu wątków, ale w przypadku dużych zadań wiele równoległych wątków przewyższa to przetwarzanie szeregowe. Należy pamiętać, że gdy liczba wątków przekracza liczbę rdzeni (4 w tym komputerze), dodatkowe wątki nie przynoszą żadnych korzyści. Nie należy oczekiwać, że prędkość będzie idealnie skalować się wraz z liczbą wątków: wielowątkowość wiąże się z pewnym obciążeniem, a większość nowoczesnych procesorów będzie działać nieco szybciej, gdy wykonywane jest tylko jedno intensywne zadanie, niż podczas wykonywania uruchomionych zadań obciążających wiele rdzeni ('turboboost').
258+Wyniki pokazują, że występuje opóźnienie w tworzeniu wątków, ale w przypadku dużych zadań wiele równoległych wątków przewyższa to przetwarzanie szeregowe. Należy pamiętać, że gdy liczba wątków przekracza liczbę rdzeni (4 w tym komputerze), dodatkowe wątki nie przynoszą żadnych korzyści. Nie należy oczekiwać, że prędkość będzie idealnie skalować się wraz z liczbą wątków: wielowątkowość wiąże się z pewnym obciążeniem, a większość nowoczesnych procesorów będzie działać nieco szybciej, gdy wykonywane jest tylko jedno intensywne zadanie, niż podczas wykonywania uruchomionych zadań obciążających wiele rdzeni ('turboboost').
268259
269- * Komputer zgłasza 4 rdzeni: to prawdopodobnie jest optymalna liczba wątków
270- * Przetworzono szeregowo 10 wartości w czasie 0ms, wykonując 10^0.5 = 3.16227766016838
271- * Przetworzono 1 wątkowo 10 wartości w czasie 3ms, wykonując 10^0.5 = 3.16227766016838
272- * Przetworzono 2 wątkowo 10 wartości w czasie 5ms, wykonując 10^0.5 = 3.16227766016838
273- * Przetworzono 4 wątkowo 10 wartości w czasie 9ms, wykonując 10^0.5 = 3.16227766016838
274- * Przetworzono 8 wątkowo 10 wartości w czasie 19ms, wykonując 10^0.5 = 3.16227766016838
275- * Przetworzono szeregowo 100000000 wartości w czasie 10214ms, wykonując 100000000^0.5 = 10000
276- * Przetworzono 1 wątkowo 100000000 wartości w czasie 10320ms, wykonując 100000000^0.5 = 10000
277- * Przetworzono 2 wątkowo 100000000 wartości w czasie 5894ms, wykonując 100000000^0.5 = 10000
278- * Przetworzono 4 wątkowo 100000000 wartości w czasie 3801ms, wykonując 100000000^0.5 = 10000
279- * Przetworzono 8 wątkowo 100000000 wartości w czasie 3733ms, wykonując 100000000^0.5 = 10000
260+- Komputer zgłasza 4 rdzeni: to prawdopodobnie jest optymalna liczba wątków
261+- Przetworzono szeregowo 10 wartości w czasie 0ms, wykonując 10^0.5 = 3.16227766016838
262+- Przetworzono 1 wątkowo 10 wartości w czasie 3ms, wykonując 10^0.5 = 3.16227766016838
263+- Przetworzono 2 wątkowo 10 wartości w czasie 5ms, wykonując 10^0.5 = 3.16227766016838
264+- Przetworzono 4 wątkowo 10 wartości w czasie 9ms, wykonując 10^0.5 = 3.16227766016838
265+- Przetworzono 8 wątkowo 10 wartości w czasie 19ms, wykonując 10^0.5 = 3.16227766016838
266+- Przetworzono szeregowo 100000000 wartości w czasie 10214ms, wykonując 100000000^0.5 = 10000
267+- Przetworzono 1 wątkowo 100000000 wartości w czasie 10320ms, wykonując 100000000^0.5 = 10000
268+- Przetworzono 2 wątkowo 100000000 wartości w czasie 5894ms, wykonując 100000000^0.5 = 10000
269+- Przetworzono 4 wątkowo 100000000 wartości w czasie 3801ms, wykonując 100000000^0.5 = 10000
270+- Przetworzono 8 wątkowo 100000000 wartości w czasie 3733ms, wykonując 100000000^0.5 = 10000
280271
281272---
282-*From the Free Pascal wiki, page "Example of multi-threaded application: array of threads/pl", as the Internet Archive kept it on 2023-12-03. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
273+*From the Free Pascal wiki, page "Example of multi-threaded application: array of threads/pl", as it stood on 2023-09-23 after a change by Slawek, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
274+Category: Multitasking
275+Category: Parallel programming
276+Category: Tutorials
277+Category: macOS

Back to history