Darmowy fragment publikacji:
Tytuł oryginału: The C++ Standard Library: A Tutorial and Reference (2nd Edition)
Tłumaczenie: Przemysław Szeremiota (wstęp, rozdz. 1 – 6, 9 – 13, 15 – 17, 19, dodatek A), Radosław
Meryk (rozdz. 8, 14, 18), Rafał Jońca (rozdz. 7) z wykorzystaniem fragmentów książki „C++.
Biblioteka standardowa. Podręcznik programisty” w tłumaczeniu Przemysława Stecia i Rafała
Szpotona
ISBN: 978-83-246-5576-2
Authorized translation from the English language edition, entitled: THE C++ STANDARD LIBRARY:
A TUTORIAL AND REFERENCE, Second Edition; ISBN 0321623215; by Nicolai M. Josuttis;
published by Pearson Education, Inc, publishing as Addison Wesley.
Copyright © 2012 Pearson Education, Inc.
All rights reserved. No part of this book may by reproduced or transmitted in any form or by any
means, electronic or mechanical, including photocopying, recording or by any information storage
retrieval system, without permission from Pearson Education, Inc.
Polish language edition published by HELION S.A. Copyright © 2014.
Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej
publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną,
fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje
naruszenie praw autorskich niniejszej publikacji.
Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich
właścicieli.
Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje
były kompletne i rzetelne. Nie bierze jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za
związane z tym ewentualne naruszenie praw patentowych lub autorskich. Wydawnictwo HELION nie
ponosi również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji
zawartych w książce.
Wydawnictwo HELION
ul. Kościuszki 1c, 44-100 GLIWICE
tel. 32 231 22 19, 32 230 98 63
e-mail: helion@helion.pl
WWW: http://helion.pl (księgarnia internetowa, katalog książek)
Pliki z przykładami omawianymi w książce można znaleźć pod adresem:
ftp://ftp.helion.pl/przyklady/cpbsp2.zip
Drogi Czytelniku!
Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres
http://helion.pl/user/opinie/cpbsp2
Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję.
Printed in Poland.
• Kup książkę
• Poleć książkę
• Oceń książkę
• Księgarnia internetowa
• Lubię to! » Nasza społeczność
Spis tre(cid:264)ci
Przedmowa do drugiego wydania
Podzi(cid:246)kowania do drugiego wydania
Przedmowa do pierwszego wydania
Podzi(cid:246)kowania do pierwszego wydania
1. O ksi(cid:241)(cid:276)ce
1.1. Dlaczego powsta(cid:228)a ta ksi(cid:241)(cid:276)ka?
1.2. Co nale(cid:276)y wiedzie(cid:232) przed przyst(cid:241)pieniem do lektury tej ksi(cid:241)(cid:276)ki?
1.3. Styl i struktura ksi(cid:241)(cid:276)ki
1.4. Jak czyta(cid:232) t(cid:246) ksi(cid:241)(cid:276)k(cid:246)?
1.5. Stan obecny
1.6. Przyk(cid:228)adowy kod i dodatkowe informacje
2. Wprowadzenie do j(cid:246)zyka C++ i biblioteki standardowej
2.1. Historia standardów C++
2.1.1. Typowe pytania o standard C++11
2.1.2. Zgodno(cid:264)(cid:232) pomi(cid:246)dzy C++11 i C++98
2.2. Z(cid:228)o(cid:276)ono(cid:264)(cid:232) algorytmów a notacja O
3. Nowe elementy j(cid:246)zyka
3.1. Nowe elementy j(cid:246)zyka C++11
3.1.1. Istotne pomniejsze porz(cid:241)dki sk(cid:228)adniowe
3.1.2. Automatyczna dedukcja typu ze s(cid:228)owem auto
3.1.3. Jednolita sk(cid:228)adnia inicjalizacji i listy inicjalizacyjne
3.1.4. P(cid:246)tle zakresowe
3.1.5. Semantyka przeniesienia i referencje do r-warto(cid:264)ci
3.1.6. Nowe litera(cid:228)y napisowe
3.1.7. S(cid:228)owo noexcept
3.1.8. S(cid:228)owo constexpr
17
19
21
23
25
25
26
26
30
30
30
31
31
32
34
34
37
37
37
38
39
41
43
48
49
51
Kup książkęPoleć książkę6
SPIS TRE(cid:285)CI
3.1.9. Nowe elementy szablonów
3.1.10. Lambdy
3.1.11. S(cid:228)owo decltype
3.1.12. Nowa sk(cid:228)adnia deklaracji funkcji
3.1.13. Klasy wyliczeniowe
3.1.14. Nowe typy podstawowe
3.2. Starsze „nowo(cid:264)ci” j(cid:246)zyka C++
3.2.1. Jawna inicjalizacja typów podstawowych
3.2.2. Definicja funkcji main()
4. Poj(cid:246)cia ogólne
4.1. Przestrze(cid:254) nazw std
4.2. Pliki nag(cid:228)ówkowe
4.3. Obs(cid:228)uga b(cid:228)(cid:246)dów i wyj(cid:241)tków
4.3.1. Standardowe klasy wyj(cid:241)tków
4.3.2. Sk(cid:228)adowe klas wyj(cid:241)tków
4.3.3. Przekazywanie wyj(cid:241)tków z u(cid:276)yciem klasy exception_ptr
4.3.4. Zg(cid:228)aszanie wyj(cid:241)tków standardowych
4.3.5. Tworzenie klas pochodnych standardowych klas wyj(cid:241)tków
4.4. Obiekty wywo(cid:228)ywalne
4.5. Wielow(cid:241)tkowo(cid:264)(cid:232) i wspó(cid:228)bie(cid:276)no(cid:264)(cid:232)
4.6. Alokatory
5. Narz(cid:246)dzia
5.1. Pary i krotki
5.1.1. Pary
5.1.2. Krotki
5.1.3. Wej(cid:264)cie-wyj(cid:264)cie dla krotek
5.1.4. Konwersje pomi(cid:246)dzy krotkami a parami
5.2. Inteligentne wska(cid:274)niki
5.2.1. Klasa shared_ptr
5.2.2. Klasa weak_ptr
5.2.3. Niepoprawne stosowanie wska(cid:274)ników wspó(cid:228)dzielonych
5.2.4. Klasy wska(cid:274)ników s(cid:228)abych i wspó(cid:228)dzielonych w szczegó(cid:228)ach
5.2.5. Klasa unique_ptr
5.2.6. Klasa unique_ptr w szczegó(cid:228)ach
5.2.7. Klasa auto_ptr
5.2.8. Podsumowanie inteligentnych wska(cid:274)ników
5.3. Ograniczenia liczbowe
5.4. Cechy typowe i narz(cid:246)dzia pracy z typami
5.4.1. Przeznaczenie cech typowych
5.4.2. Cechy typowe w szczegó(cid:228)ach
5.4.3. Uj(cid:246)cia referencyjne
5.4.4. Uj(cid:246)cia typów funkcyjnych
5.5. Funkcje pomocnicze
5.5.1. Obliczanie warto(cid:264)ci minimalnej oraz maksymalnej
5.5.2. Zamiana dwóch warto(cid:264)ci
5.5.3. Dodatkowe operatory porównania
5.6. Statyczna arytmetyka liczb wymiernych — klasa ratio
51
53
57
57
58
58
59
63
63
65
65
67
68
68
72
80
80
81
82
83
85
87
88
88
96
101
103
103
104
112
118
120
127
140
143
144
145
152
152
156
163
164
165
165
167
169
170
Kup książkęPoleć książkęC++. BIBLIOTEKA STANDARDOWA. PODR(cid:265)CZNIK PROGRAMISTY
7
5.7. Zegary i czasomierze
5.8. Pliki nag(cid:228)ówkowe cstddef , cstdlib oraz cstring
5.8.1. Definicje w pliku cstddef
5.8.2. Definicje w pliku cstdlib
5.8.3. Definicje w pliku cstring
6. Standardowa biblioteka szablonów (STL)
6.1. Sk(cid:228)adniki biblioteki STL
6.2. Kontenery
6.2.1. Kontenery sekwencyjne
6.2.2. Kontenery asocjacyjne
6.2.3. Kontenery nieporz(cid:241)dkuj(cid:241)ce
6.2.4. Tablice asocjacyjne
6.2.5. Inne kontenery
6.2.6. Adaptatory kontenerów
5.7.1. Przegl(cid:241)d biblioteki chrono
5.7.2. Okresy
5.7.3. Zegary i punkty w czasie
5.7.4. Funkcje daty i czasu j(cid:246)zyka C i standardu POSIX
5.7.5. Czasowe wstrzymywanie wykonania
174
174
176
180
189
191
192
193
193
195
197
198
199
201
210
214
219
221
222
223
6.3.1. Inne przyk(cid:228)ady u(cid:276)ycia kontenerów asocjacyjnych i nieporz(cid:241)dkuj(cid:241)cych 228
6.3.2. Kategorie iteratorów
233
234
238
242
245
245
247
249
251
251
252
253
256
258
259
259
261
263
267
267
273
274
277
278
278
279
6.10.1. Definicja obiektów funkcyjnych
6.10.2. Predefiniowane obiekty funkcyjne
6.10.3. Wi(cid:241)zanie wywo(cid:228)ania
6.10.4. Obiekty funkcyjne i wi(cid:241)zanie kontra lambdy
6.5.1. Iteratory wstawiaj(cid:241)ce
6.5.2. Iteratory strumieni
6.5.3. Iteratory odwrotne
6.5.4. Iteratory przenosz(cid:241)ce
6.7.1. Usuwanie elementów
6.7.2. Algorytmy modyfikuj(cid:241)ce kontenery asocjacyjne i nieporz(cid:241)dkuj(cid:241)ce
6.7.3. Algorytmy a funkcje sk(cid:228)adowe
6.8. Funkcje jako argumenty algorytmów
6.8.1. Przyk(cid:228)ady u(cid:276)ycia funkcji jako argumentów algorytmów
6.8.2. Predykaty
6.4. Algorytmy
6.4.1. Zakresy
6.4.2. Obs(cid:228)uga wielu zakresów
6.5. Adaptatory iteratorów
6.11. Elementy kontenerów
6.11.1. Wymagania wobec elementów kontenerów
6.11.2. Semantyka warto(cid:264)ci a semantyka referencji
6.6. W(cid:228)asne uogólnione operacje na kontenerach
6.7. Algorytmy modyfikuj(cid:241)ce
6.3. Iteratory
6.9. Stosowanie lambd
6.10. Obiekty funkcyjne
Kup książkęPoleć książkę8
SPIS TRE(cid:285)CI
6.12. Obs(cid:228)uga b(cid:228)(cid:246)dów i wyj(cid:241)tków wewn(cid:241)trz biblioteki STL
6.12.1. Obs(cid:228)uga b(cid:228)(cid:246)dów
6.12.2. Obs(cid:228)uga wyj(cid:241)tków
6.13. Rozbudowa biblioteki STL
6.13.1. Integrowanie dodatkowych typów
6.13.2. Dziedziczenie po typach STL
7. Kontenery STL
7.1. Wspólne cechy i operacje kontenerów
7.1.1. Wspólne cechy kontenerów
7.1.2. Wspólne operacje kontenerów
7.1.3. Typy kontenerów
7.2. Tablice
7.2.1. Mo(cid:276)liwo(cid:264)ci tablic
7.2.2. Operacje dotycz(cid:241)ce tablic
7.2.3. U(cid:276)ywanie klas array jako zwyk(cid:228)ych tablic
7.2.4. Obs(cid:228)uga wyj(cid:241)tków
7.2.5. Interfejs krotki
7.2.6. Przyk(cid:228)ady u(cid:276)ycia tablic
7.3. Wektory
7.3.1. Mo(cid:276)liwo(cid:264)ci wektorów
7.3.2. Operacje na wektorach
7.3.3. U(cid:276)ywanie wektorów jako zwyk(cid:228)ych tablic
7.3.4. Obs(cid:228)uga wyj(cid:241)tków
7.3.5. Przyk(cid:228)ady u(cid:276)ycia wektorów
7.3.6. Klasa vector bool
7.4. Kolejki o dwóch ko(cid:254)cach
7.4.1. Mo(cid:276)liwo(cid:264)ci kolejek deque
7.4.2. Operacje na kolejkach deque
7.4.3. Obs(cid:228)uga wyj(cid:241)tków
7.4.4. Przyk(cid:228)ady u(cid:276)ycia kolejek deque
7.5. Listy
7.5.1. Mo(cid:276)liwo(cid:264)ci list
7.5.2. Operacje na listach
7.5.3. Obs(cid:228)uga wyj(cid:241)tków
7.5.4. Przyk(cid:228)ady u(cid:276)ycia list
7.6. Listy jednokierunkowe
7.6.1. Mo(cid:276)liwo(cid:264)ci list jednokierunkowych
7.6.2. Operacje na listach jednokierunkowych
7.6.3. Obs(cid:228)uga wyj(cid:241)tków
7.6.4. Przyk(cid:228)ady u(cid:276)ycia list jednokierunkowych
7.7. Zbiory i wielozbiory
7.7.1. Mo(cid:276)liwo(cid:264)ci zbiorów i wielozbiorów
7.7.2. Operacje na zbiorach i wielozbiorach
7.7.3. Obs(cid:228)uga wyj(cid:241)tków
7.7.4. Przyk(cid:228)ady u(cid:276)ycia zbiorów i wielozbiorów
7.7.5. Przyk(cid:228)ad okre(cid:264)lania kryterium sortowania podczas wykonywania
7.8. Mapy oraz multimapy
7.8.1. Mo(cid:276)liwo(cid:264)ci map oraz multimap
7.8.2. Operacje na mapach oraz multimapach
280
280
282
286
286
287
289
290
290
290
297
298
298
301
305
305
306
306
307
308
310
316
317
318
319
321
322
323
327
327
328
329
330
336
337
338
339
341
351
351
352
354
355
364
364
367
369
370
371
Kup książkęPoleć książkęC++. BIBLIOTEKA STANDARDOWA. PODR(cid:265)CZNIK PROGRAMISTY
7.8.3. Zastosowanie map jako tablic asocjacyjnych
7.8.4. Obs(cid:228)uga wyj(cid:241)tków
7.8.5. Przyk(cid:228)ady u(cid:276)ycia map i multimap
7.8.6. Przyk(cid:228)ad z mapami, (cid:228)a(cid:254)cuchami oraz definiowaniem
kryterium sortowania podczas wykonywania
7.9. Kontenery nieuporz(cid:241)dkowane
7.9.1. Mo(cid:276)liwo(cid:264)ci kontenerów nieuporz(cid:241)dkowanych
7.9.2. Tworzenie i kontrolowanie kontenerów nieuporz(cid:241)dkowanych
7.9.3. Inne operacje kontenerów nieuporz(cid:241)dkowanych
7.9.4. Interfejs kube(cid:228)ków
7.9.5. Zastosowanie map nieuporz(cid:241)dkowanych jako tablic asocjacyjnych
7.9.6. Obs(cid:228)uga wyj(cid:241)tków
7.9.7. Przyk(cid:228)ady u(cid:276)ycia kontenerów nieuporz(cid:241)dkowanych
7.10. Inne kontenery STL
7.10.1. (cid:227)a(cid:254)cuchy jako kontenery STL
7.10.2. Zwyk(cid:228)e tablice jako kontenery STL
7.11. Implementacja semantyki referencji
7.12. Kiedy stosowa(cid:232) poszczególne kontenery?
8. Sk(cid:228)adowe kontenerów STL
8.1. Definicje typów
8.2. Operacje tworzenia, kopiowania i niszczenia
8.3. Operacje niemodyfikuj(cid:241)ce
8.3.1. Operacje dotycz(cid:241)ce rozmiaru
8.3.2. Operacje porównania
8.3.3. Operacje niemodyfikuj(cid:241)ce kontenerów asocjacyjnych
i nieuporz(cid:241)dkowanych
8.4. Operacje przypisania
8.5. Bezpo(cid:264)redni dost(cid:246)p do elementów
8.6. Operacje generuj(cid:241)ce iteratory
8.7. Wstawianie i usuwanie elementów
8.7.1. Wstawianie pojedynczych elementów
8.7.2. Wstawianie wielu elementów
8.7.3. Usuwanie elementów
8.7.4. Zmiana rozmiaru
8.8. Specjalne funkcje sk(cid:228)adowe list
8.8.1. Specjalne funkcje sk(cid:228)adowe list i list forward_list
8.8.2. Specjalne funkcje sk(cid:228)adowe list forward_list
8.9. Interfejsy strategii obs(cid:228)ugi kontenerów
8.9.1. Niemodyfikuj(cid:241)ce funkcje strategii
8.9.2. Modyfikuj(cid:241)ce funkcje strategii
8.9.3. Interfejs dost(cid:246)pu do kube(cid:228)ków kontenerów nieuporz(cid:241)dkowanych
8.10. Obs(cid:228)uga alokatorów
8.10.1. Podstawowe sk(cid:228)adowe alokatorów
8.10.2. Konstruktory z opcjonalnymi parametrami alokatorów
9
382
384
384
388
391
394
397
404
411
412
413
413
421
422
423
425
428
435
435
438
442
442
443
444
446
448
450
452
452
457
459
462
462
462
466
470
470
471
473
474
474
474
Kup książkęPoleć książkę10
9.
Iteratory STL
9.1. Pliki nag(cid:228)ówkowe iteratorów
9.2. Kategorie iteratorów
9.2.1. Iteratory wyj(cid:264)ciowe
9.2.2. Iteratory wej(cid:264)ciowe
9.2.3. Iteratory post(cid:246)puj(cid:241)ce
9.2.4. Iteratory dwukierunkowe
9.2.5. Iteratory dost(cid:246)pu swobodnego
9.2.6. Problem z inkrementacj(cid:241) i dekrementacj(cid:241) iteratorów wektorów
9.3. Pomocnicze funkcje iteratorów
9.3.1. Funkcja advance()
9.3.2. Funkcje next() i prev()
9.3.3. Funkcja distance()
9.3.4. Funkcja iter_swap()
9.4. Adaptatory iteratorów
9.4.1. Iteratory odwrotne
9.4.2. Iteratory wstawiaj(cid:241)ce
9.4.3. Iteratory strumieni
9.4.4. Iteratory przenosz(cid:241)ce
9.5. Cechy typowe iteratorów
9.5.1. Definiowanie uogólnionych funkcji dla iteratorów
9.6. Iteratory definiowane przez u(cid:276)ytkownika
10. Obiekty funkcyjne STL i lambdy
10.1. Poj(cid:246)cie obiektów funkcyjnych
10.1.1. Obiekty funkcyjne jako kryteria sortowania
10.1.2. Obiekty funkcyjne ze stanem wewn(cid:246)trznym
10.1.3. Warto(cid:264)(cid:232) zwracana algorytmu for_each()
10.1.4. Predykaty a obiekty funkcyjne
10.2. Predefiniowane obiekty funkcyjne i obiekty wi(cid:241)zania wywo(cid:228)ania
10.2.1. Predefiniowane obiekty funkcyjne
10.2.2. Adaptatory i obiekty wi(cid:241)zania wywo(cid:228)ania
10.2.3. Obiekty funkcyjne definiowane przez u(cid:276)ytkownika
dla adaptatorów funkcji
SPIS TRE(cid:285)CI
479
479
479
480
481
483
484
484
487
488
488
490
492
493
494
495
499
506
511
512
514
516
521
521
522
524
527
529
531
531
532
10.3. Lambdy
10.2.4. Zarzucone adaptatory funkcji
541
542
544
544
10.3.1. Lambdy a wi(cid:241)zanie wywo(cid:228)ania
545
10.3.2. Lambdy a stanowe obiekty funkcyjne
10.3.3. Lambdy z wywo(cid:228)aniami funkcji globalnych i sk(cid:228)adowych
547
10.3.4. Lambdy jako funkcje mieszaj(cid:241)ce, sortuj(cid:241)ce i kryteria równowa(cid:276)no(cid:264)ci 549
551
551
552
552
553
564
566
570
11.2.1. Krótkie wprowadzenie
11.2.2. Klasyfikacja algorytmów
11.3. Funkcje pomocnicze
11.4. Algorytm for_each()
11.5. Algorytmy niemodyfikuj(cid:241)ce
11. Algorytmy STL
11.1. Pliki nag(cid:228)ówkowe algorytmów
11.2. Przegl(cid:241)d algorytmów
Kup książkęPoleć książkęC++. BIBLIOTEKA STANDARDOWA. PODR(cid:265)CZNIK PROGRAMISTY
11.5.1. Zliczanie elementów
11.5.2. Warto(cid:264)(cid:232) minimalna i maksymalna
11.5.3. Wyszukiwanie elementów
11.5.4. Porównywanie zakresów
11.5.5. Predykaty zakresowe
11.6. Algorytmy modyfikuj(cid:241)ce
11.6.1. Kopiowanie elementów
11.6.2. Przenoszenie elementów mi(cid:246)dzy zakresami
11.6.3. Przekszta(cid:228)cenia i kombinacje elementów
11.6.4. Wymienianie elementów
11.6.5. Przypisywanie nowych warto(cid:264)ci
11.6.6. Zast(cid:246)powanie elementów
11.7. Algorytmy usuwaj(cid:241)ce
11.7.1. Usuwanie okre(cid:264)lonych warto(cid:264)ci
11.7.2. Usuwanie powtórze(cid:254)
11.8. Algorytmy mutuj(cid:241)ce
11.8.1. Odwracanie kolejno(cid:264)ci elementów
11.8.2. Przesuni(cid:246)cia cykliczne elementów
11.8.3. Permutacje elementów
11.8.4. Tasowanie elementów
11.8.5. Przenoszenie elementów na pocz(cid:241)tek
11.8.6. Podzia(cid:228) na dwa podzakresy
11.9. Algorytmy sortuj(cid:241)ce
11.9.1. Sortowanie wszystkich elementów
11.9.2. Sortowanie cz(cid:246)(cid:264)ciowe
11.9.3. Sortowanie wed(cid:228)ug n-tego elementu
11.9.4. Algorytmy stogowe
11.10. Algorytmy przeznaczone dla zakresów posortowanych
11.10.1. Wyszukiwanie elementów
11.10.2. Scalanie elementów
11.11. Algorytmy numeryczne
11.11.1. Obliczanie warto(cid:264)ci
11.11.2. Konwersje warto(cid:264)ci wzgl(cid:246)dnych i bezwzgl(cid:246)dnych
12. Kontenery specjalne
12.1. Stosy
12.1.1. Interfejs
12.1.2. Przyk(cid:228)ad u(cid:276)ycia stosów
12.1.3. W(cid:228)asna klasa stosu
12.1.4. Klasa stack w szczegó(cid:228)ach
12.2. Kolejki
12.2.1. Interfejs
12.2.2. Przyk(cid:228)ad u(cid:276)ycia kolejek
12.2.3. W(cid:228)asna klasa kolejki
12.2.4. Klasa queue w szczegó(cid:228)ach
12.3. Kolejki priorytetowe
12.3.1. Interfejs
12.3.2. Przyk(cid:228)ad u(cid:276)ycia kolejek priorytetowych
12.3.3. Klasa priority_queue w szczegó(cid:228)ach
11
570
572
574
586
593
599
600
603
605
608
610
613
615
616
619
623
623
624
627
629
631
633
634
635
637
640
642
645
646
651
659
660
663
667
668
669
669
670
673
673
675
675
676
676
676
678
678
679
Kup książkęPoleć książkę12
SPIS TRE(cid:285)CI
12.4. Adaptatory kontenerów w szczegó(cid:228)ach
12.4.1. Definicje typów
12.4.2. Konstruktory
12.4.3. Konstruktory pomocnicze dla kolejki priorytetowej
12.4.4. Operacje
12.5. Kontener bitset
12.5.1. Przyk(cid:228)ady u(cid:276)ycia kontenerów bitset
12.5.2. Klasa bitset w szczegó(cid:228)ach
13. (cid:227)a(cid:254)cuchy znakowe
13.1. Przeznaczenie klas (cid:228)a(cid:254)cuchów znakowych
13.1.1. Przyk(cid:228)ad pierwszy: konstruowanie tymczasowej nazwy pliku
13.1.2. Przyk(cid:228)ad drugi: wyodr(cid:246)bnianie s(cid:228)ów
i wypisywanie ich w odwrotnej kolejno(cid:264)ci
13.2. Opis klas reprezentuj(cid:241)cych (cid:228)a(cid:254)cuchy znakowe
13.2.1. Typy (cid:228)a(cid:254)cuchów znakowych
13.2.2. Przegl(cid:241)d funkcji sk(cid:228)adowych
13.2.3. Konstruktory oraz destruktory
13.2.4. (cid:227)a(cid:254)cuchy znakowe zwyk(cid:228)e oraz j(cid:246)zyka C
13.2.5. Rozmiar oraz pojemno(cid:264)(cid:232)
13.2.6. Dost(cid:246)p do elementów
13.2.7. Porównania
13.2.8. Modyfikatory
13.2.9. Konkatenacja (cid:228)a(cid:254)cuchów znakowych oraz ich fragmentów
13.2.10. Operatory wej(cid:264)cia-wyj(cid:264)cia
13.2.11. Poszukiwanie oraz odnajdywanie (cid:228)a(cid:254)cuchów znakowych
13.2.12. Warto(cid:264)(cid:232) npos
13.2.13. Konwersje liczbowe
13.2.14. Obs(cid:228)uga iteratorów (cid:228)a(cid:254)cuchów znakowych
13.2.15. Obs(cid:228)uga standardów narodowych
13.2.16. Wydajno(cid:264)(cid:232)
13.2.17. (cid:227)a(cid:254)cuchy znakowe a wektory
13.3. Klasa string w szczegó(cid:228)ach
13.3.1. Definicje typu oraz warto(cid:264)ci statyczne
13.3.2. Funkcje sk(cid:228)adowe s(cid:228)u(cid:276)(cid:241)ce do tworzenia, kopiowania
oraz usuwania (cid:228)a(cid:254)cuchów znakowych
13.3.3. Funkcje dotycz(cid:241)ce rozmiaru oraz pojemno(cid:264)ci
13.3.4. Porównania
13.3.5. Dost(cid:246)p do znaków
13.3.6. Tworzenie (cid:228)a(cid:254)cuchów znakowych j(cid:246)zyka C oraz tablic znaków
13.3.7. Funkcje do modyfikacji zawarto(cid:264)ci (cid:228)a(cid:254)cuchów znakowych
13.3.8. Wyszukiwanie
13.3.9. (cid:227)(cid:241)czenie (cid:228)a(cid:254)cuchów znakowych
13.3.10. Funkcje wej(cid:264)cia-wyj(cid:264)cia
13.3.11. Konwersje liczbowe
13.3.12. Funkcje tworz(cid:241)ce iteratory
13.3.13. Obs(cid:228)uga alokatorów
680
680
681
681
682
684
686
688
689
690
691
695
699
699
700
704
705
707
708
710
711
714
715
716
719
720
722
728
730
730
731
731
732
734
735
737
739
739
748
752
753
754
755
756
Kup książkęPoleć książkęC++. BIBLIOTEKA STANDARDOWA. PODR(cid:265)CZNIK PROGRAMISTY
14. Wyra(cid:276)enia regularne
14.1. Interfejs dopasowywania i wyszukiwania wyra(cid:276)e(cid:254) regularnych
14.2. Obs(cid:228)uga podwyra(cid:276)e(cid:254)
14.3. Iteratory dopasowa(cid:254)
14.4. Iteratory podci(cid:241)gów
14.5. Zast(cid:246)powanie wyra(cid:276)e(cid:254) regularnych
14.6. Flagi wyra(cid:276)e(cid:254) regularnych
14.7. Wyj(cid:241)tki zwi(cid:241)zane z wyra(cid:276)eniami regularnymi
14.8. Gramatyka wyra(cid:276)e(cid:254) regularnych ECMAScript
14.9. Inne gramatyki
14.10. Sygnatury podstawowych funkcji
15. Obs(cid:228)uga wej(cid:264)cia-wyj(cid:264)cia
z wykorzystaniem klas strumieniowych
15.1. Podstawy strumieni wej(cid:264)cia-wyj(cid:264)cia
15.1.1. Obiekty strumieni
15.1.2. Klasy strumieni
15.1.3. Globalne obiekty strumieni
15.1.4. Operatory strumieniowe
15.1.5. Manipulatory
15.1.6. Prosty przyk(cid:228)ad
15.2. Podstawowe obiekty oraz klasy strumieniowe
15.2.1. Klasy oraz hierarchia klas
15.2.2. Globalne obiekty strumieni
15.2.3. Pliki nag(cid:228)ówkowe
15.3. Standardowe operatory strumieniowe oraz
15.3.1. Operator wyj(cid:264)ciowy
15.3.2. Operator wej(cid:264)ciowy
15.3.3. Operacje wej(cid:264)cia-wyj(cid:264)cia dla specjalnych typów
15.4. Stany strumieni
15.4.1. Sta(cid:228)e s(cid:228)u(cid:276)(cid:241)ce do okre(cid:264)lania stanów strumieni
15.4.2. Funkcje sk(cid:228)adowe operuj(cid:241)ce na stanie strumieni
15.4.3. Warunki wykorzystuj(cid:241)ce stan strumienia oraz warto(cid:264)ci logiczne
15.4.4. Stan strumienia i wyj(cid:241)tki
15.5. Standardowe funkcje wej(cid:264)cia-wyj(cid:264)cia
15.5.1. Funkcje sk(cid:228)adowe s(cid:228)u(cid:276)(cid:241)ce do pobierania danych
15.5.2. Funkcje sk(cid:228)adowe s(cid:228)u(cid:276)(cid:241)ce do wysy(cid:228)ania danych
15.5.3. Przyk(cid:228)ad u(cid:276)ycia
15.5.4. Obiekty sentry
15.6. Manipulatory
15.6.1. Przegl(cid:241)d dost(cid:246)pnych manipulatorów
15.6.2. Sposób dzia(cid:228)ania manipulatorów
15.6.3. Manipulatory definiowane przez u(cid:276)ytkownika
15.7. Formatowanie
15.7.1. Znaczniki formatu
15.7.2. Format warto(cid:264)ci logicznych
15.7.3. Szeroko(cid:264)(cid:232) pola, znak wype(cid:228)nienia oraz wyrównanie
15.7.4. Znak warto(cid:264)ci dodatnich oraz du(cid:276)e litery
15.7.5. Podstawa numeryczna
13
759
759
762
768
769
771
773
777
779
781
782
785
786
786
787
787
788
788
789
790
790
794
795
796
796
798
799
802
802
803
805
808
812
813
817
818
819
820
820
822
824
825
825
827
828
831
832
Kup książkęPoleć książkę14
SPIS TRE(cid:285)CI
15.7.6. Notacja zapisu liczb zmiennoprzecinkowych
15.7.7. Ogólne definicje formatuj(cid:241)ce
15.8. Umi(cid:246)dzynarodawianie
15.9. Dost(cid:246)p do plików
15.9.1. Klasy strumieni plikowych
15.9.2. Semantyka r-warto(cid:264)ci i przeniesienia dla strumieni plikowych
15.9.3. Znaczniki pliku
15.9.4. Dost(cid:246)p swobodny
15.9.5. Deskryptory plików
15.10. Klasy strumieni dla (cid:228)a(cid:254)cuchów znakowych
15.10.1. Klasy strumieni dla (cid:228)a(cid:254)cuchów znakowych
15.10.2. Semantyka przeniesienia dla strumieni z (cid:228)a(cid:254)cuchów znakowych
15.10.3. Klasy strumieni dla warto(cid:264)ci typu char*
15.11. Operatory wej(cid:264)cia-wyj(cid:264)cia dla typów zdefiniowanych przez u(cid:276)ytkownika
15.11.1. Implementacja operatorów wyj(cid:264)ciowych
15.11.2. Implementacja operatorów wej(cid:264)ciowych
15.11.3. Operacje wej(cid:264)cia-wyj(cid:264)cia przy u(cid:276)yciu funkcji pomocniczych
15.11.4. Znaczniki formatu definiowane przez u(cid:276)ytkownika
15.11.5. Konwencje operatorów wej(cid:264)cia-wyj(cid:264)cia definiowanych
przez u(cid:276)ytkownika
15.12. (cid:227)(cid:241)czenie strumieni wej(cid:264)ciowych oraz wyj(cid:264)ciowych
15.12.1. Lu(cid:274)ne powi(cid:241)zanie przy u(cid:276)yciu tie()
15.12.2. (cid:263)cis(cid:228)e powi(cid:241)zanie przy u(cid:276)yciu buforów strumieni
15.12.3. Przekierowywanie strumieni standardowych
15.12.4. Strumienie s(cid:228)u(cid:276)(cid:241)ce do odczytu oraz zapisu
15.13. Klasy bufora strumienia
15.13.1. Interfejsy buforów strumieni
15.13.2. Iteratory wykorzystywane z buforem strumienia
15.13.3. Bufory strumienia definiowane przez u(cid:276)ytkownika
15.14. Kwestie wydajno(cid:264)ci
15.14.1. Synchronizacja ze standardowymi strumieniami j(cid:246)zyka C
15.14.2. Buforowanie w buforach strumieni
15.14.3. Bezpo(cid:264)rednie wykorzystanie buforów strumieni
16. Umi(cid:246)dzynarodowienie
16.1. Kodowanie znaków i zestawy znaków
16.1.1. Znaki wielobajtowe i znaki szerokiego zakresu
16.1.2. Ró(cid:276)ne zestawy znaków
16.1.3. Obs(cid:228)uga zestawów znaków w C++
16.1.4. Cechy znaków
16.1.5. Umi(cid:246)dzynarodawianie specjalnych znaków
16.2. Poj(cid:246)cie obiektów ustawie(cid:254) lokalnych
16.2.1. Wykorzystywanie ustawie(cid:254) lokalnych
16.2.2. Aspekty ustawie(cid:254) lokalnych
16.3. Klasa locale w szczegó(cid:228)ach
16.4. Klasa facet w szczegó(cid:228)ach
16.4.1. Formatowanie warto(cid:264)ci liczbowych
16.4.2. Formatowanie warto(cid:264)ci pieni(cid:246)(cid:276)nych
16.4.3. Formatowanie czasu oraz daty
834
836
837
838
838
842
843
847
849
850
851
854
855
858
858
860
862
863
866
867
867
869
871
872
874
875
877
881
893
893
894
895
899
901
901
902
903
904
908
909
911
917
919
922
923
928
938
Kup książkęPoleć książkęC++. BIBLIOTEKA STANDARDOWA. PODR(cid:265)CZNIK PROGRAMISTY
16.4.4. Klasyfikacja oraz konwersja znaków
16.4.5. Sortowanie (cid:228)a(cid:254)cuchów znakowych
16.4.6. Lokalizacja komunikatów
17. Komponenty numeryczne
17.1. Liczby i rozk(cid:228)ady losowe
17.1.1. Pierwszy przyk(cid:228)ad
17.1.2. Mechanizmy losowo(cid:264)ci
17.1.3. Mechanizmy losowo(cid:264)ci w szczegó(cid:228)ach
17.1.4. Rozk(cid:228)ady
17.1.5. Dystrybucje w szczegó(cid:228)ach
17.2. Liczby zespolone
17.2.1. complex w uj(cid:246)ciu ogólnym
17.2.2. Przyk(cid:228)ad wykorzystania klasy reprezentuj(cid:241)cej liczby zespolone
17.2.3. Funkcje operuj(cid:241)ce na liczbach zespolonych
17.2.4. Klasa complex w szczegó(cid:228)ach
17.3. Globalne funkcje numeryczne
17.4. Klasa valarray
18. Wspó(cid:228)bie(cid:276)no(cid:264)(cid:232)
18.1. Interfejs wysokiego poziomu: async() i futury
18.1.1. Pierwszy przyk(cid:228)ad u(cid:276)ycia funkcji async() i futur
18.1.2. Przyk(cid:228)ad oczekiwania na dwa zadania
18.1.3. Wspó(cid:228)dzielone futury
18.2. Interfejs niskiego poziomu: w(cid:241)tki i promesy
18.2.1. Klasa std::thread
18.2.2. Promesy
18.2.3. Klasa packaged_task
18.3. Uruchamianie w(cid:241)tku w szczegó(cid:228)ach
18.3.1. Funkcja async() w szczegó(cid:228)ach
18.3.2. Futury w szczegó(cid:228)ach
18.3.3. Futury wspó(cid:228)dzielone w szczegó(cid:228)ach
18.3.4. Klasa std::promise w szczegó(cid:228)ach
18.3.5. Klasa std::packaged_task w szczegó(cid:228)ach
18.3.6. Klasa std::thread w szczegó(cid:228)ach
18.3.7. Przestrze(cid:254) nazw this_thread
18.4. Synchronizacja w(cid:241)tków, czyli najwi(cid:246)kszy problem wspó(cid:228)bie(cid:276)no(cid:264)ci
18.4.1. Uwaga na wspó(cid:228)bie(cid:276)no(cid:264)(cid:232)!
18.4.2. Przyczyna problemu jednoczesnego dost(cid:246)pu do danych
18.4.3. Zakres problemu, czyli co mo(cid:276)e pój(cid:264)(cid:232) (cid:274)le?
18.4.4. Mechanizmy pozwalaj(cid:241)ce na rozwi(cid:241)zanie problemów
18.5. Muteksy i blokady
18.5.1. Wykorzystywanie muteksów i blokad
18.5.2. Muteksy i blokady w szczegó(cid:228)ach
18.5.3. Wywo(cid:228)ywanie funkcjonalno(cid:264)ci raz dla wielu w(cid:241)tków
18.6. Zmienne warunkowe
18.6.1. Przeznaczenie zmiennych warunkowych
18.6.2. Pierwszy kompletny przyk(cid:228)ad
wykorzystania zmiennych warunkowych
15
945
959
960
963
963
964
969
972
973
977
981
981
982
984
992
997
999
1001
1003
1003
1013
1017
1021
1021
1027
1029
1030
1031
1033
1034
1035
1036
1038
1039
1040
1041
1042
1043
1047
1049
1049
1058
1062
1063
1064
1065
Kup książkęPoleć książkę16
18.6.3. Wykorzystanie zmiennych warunkowych
do zaimplementowania kolejki dla wielu w(cid:241)tków
18.6.4. Zmienne warunkowe w szczegó(cid:228)ach
18.7. Atomowe typy danych
18.7.1. Przyk(cid:228)ad u(cid:276)ycia atomowych typów danych
18.7.2. Atomowe typy danych i ich interfejs wysokiego poziomu
w szczegó(cid:228)ach
18.7.3. Interfejs atomowych typów danych w stylu j(cid:246)zyka C
18.7.4. Niskopoziomowy interfejs atomowych typów danych
19. Alokatory
19.1. Wykorzystywanie alokatorów przez programistów aplikacji
19.2. Alokator definiowany przez u(cid:276)ytkownika
19.3. Wykorzystywanie alokatorów przez programistów bibliotek
Bibliografia
Grupy i fora dyskusyjne
Ksi(cid:241)(cid:276)ki i strony WWW
Skorowidz
SPIS TRE(cid:285)CI
1067
1070
1072
1073
1077
1080
1081
1085
1085
1086
1088
1093
1093
1094
1099
Kup książkęPoleć książkę18
Wspó(cid:228)bie(cid:276)no(cid:264)(cid:232)
Nowoczesne architektury systemów zazwyczaj umo(cid:276)liwiaj(cid:241) uruchamianie wielu
zada(cid:254) i wielu w(cid:241)tków jednocze(cid:264)nie. Wykorzystanie wielu w(cid:241)tków mo(cid:276)e przy-
czyni(cid:232) si(cid:246) do znacznej poprawy czasu realizacji programów, zw(cid:228)aszcza w kom-
puterach, których procesory s(cid:241) wyposa(cid:276)one w wiele rdzeni.
Jednak równoleg(cid:228)e uruchamianie programów wprowadza równie(cid:276) nowe wy-
zwania. Zamiast wykonywa(cid:232) instrukcje jedna po drugiej, mo(cid:276)na uruchomi(cid:232) wiele
instrukcji jednocze(cid:264)nie. Mo(cid:276)e to prowadzi(cid:232) do problemów z jednoczesnym dost(cid:246)-
pem do tych samych zasobów. W takich sytuacjach operacje tworzenia, czytania,
pisania i usuwania mog(cid:241) odbywa(cid:232) si(cid:246) w nieoczekiwanej kolejno(cid:264)ci, co mo(cid:276)e prowa-
dzi(cid:232) do nieoczekiwanych rezultatów. W rzeczywisto(cid:264)ci wspó(cid:228)bie(cid:276)ny dost(cid:246)p do
danych z wielu w(cid:241)tków (cid:228)atwo mo(cid:276)e sta(cid:232) si(cid:246) koszmarem. Jednym z najprostszych
problemów, jakie mog(cid:241) si(cid:246) pojawi(cid:232), s(cid:241) zakleszczenia, kiedy to w(cid:241)tki wzajemnie
na siebie czekaj(cid:241).
Przed wprowadzeniem standardu C++11 j(cid:246)zyk C++ ani standardowa biblioteka
j(cid:246)zyka nie zawiera(cid:228)y obs(cid:228)ugi wspó(cid:228)bie(cid:276)no(cid:264)ci, jednak poszczególne implementacje
mog(cid:228)y zawiera(cid:232) pewne mechanizmy obs(cid:228)ugi wspó(cid:228)bie(cid:276)no(cid:264)ci. Wraz z powsta-
niem C++11 to si(cid:246) zmieni(cid:228)o. Usprawniono zarówno rdze(cid:254) j(cid:246)zyka, jak i bibliotek(cid:246),
wprowadzaj(cid:241)c obs(cid:228)ug(cid:246) programowania wspó(cid:228)bie(cid:276)nego (patrz podrozdzia(cid:228) 4.5):
(cid:120) W podstawowym j(cid:246)zyku zdefiniowano model pami(cid:246)ci, który gwarantuje, (cid:276)e
aktualizacje dwóch ró(cid:276)nych obiektów wykorzystywanych przez dwa ró(cid:276)ne
w(cid:241)tki s(cid:241) niezale(cid:276)ne od siebie. Wprowadzono równie(cid:276) nowe s(cid:228)owo kluczowe
thread_local do definiowania zmiennych z warto(cid:264)ciami specyficznymi dla
w(cid:241)tków.
(cid:120) Biblioteka zapewnia obecnie wsparcie dla uruchamiania wielu w(cid:241)tków.
Umo(cid:276)liwia równie(cid:276) przekazywanie argumentów, zwracanie warto(cid:264)ci i zg(cid:228)a-
szanie wyj(cid:241)tków poza granicami w(cid:241)tków. Zapewnia tak(cid:276)e mechanizmy do
synchronizacji wielu w(cid:241)tków. Dzi(cid:246)ki temu mo(cid:276)emy synchronizowa(cid:232) zarów-
no przep(cid:228)yw sterowania, jak i dost(cid:246)p do danych.
Kup książkęPoleć książkę1002
18. WSPÓ(cid:224)BIE(cid:297)NO(cid:285)(cid:251)
Biblioteka zapewnia wsparcie dla wspó(cid:228)bie(cid:276)no(cid:264)ci na wielu poziomach. Na przy-
k(cid:228)ad interfejs wysokiego poziomu umo(cid:276)liwia rozpocz(cid:246)cie w(cid:241)tku, przekazanie do
niego argumentów i obs(cid:228)ug(cid:246) wyników i wyj(cid:241)tków. Interfejs ten bazuje na kilku in-
terfejsach niskiego poziomu dla ka(cid:276)dego z tych aspektów. Z drugiej strony, ist-
niej(cid:241) równie(cid:276) w(cid:228)asno(cid:264)ci niskiego poziomu, takie jak muteksy lub atomowe typy da-
nych (ang. atomics) obs(cid:228)uguj(cid:241)ce swobodne kolejkowanie pami(cid:246)ci (ang. relaxed memory
order).
W tym rozdziale zaprezentowano te funkcje biblioteczne. Nale(cid:276)y zauwa(cid:276)y(cid:232),
(cid:276)e temat wspó(cid:228)bie(cid:276)no(cid:264)ci oraz opis bibliotek, które j(cid:241) obs(cid:228)uguj(cid:241), mo(cid:276)e wype(cid:228)ni(cid:232)
ca(cid:228)e tomy. Z tego powodu w tym rozdziale zaprezentuj(cid:246) ogólne poj(cid:246)cia i typowe
przyk(cid:228)ady dla przeci(cid:246)tnego programisty, z g(cid:228)ównym naciskiem na interfejsy wy-
sokiego poziomu.
Po szczegó(cid:228)owe informacje, zw(cid:228)aszcza dotycz(cid:241)ce trudnych problemów funkcji
i interfejsów niskiego poziomu, odsy(cid:228)am do wymienionych tutaj konkretnych
ksi(cid:241)(cid:276)ek i artyku(cid:228)ów. Moj(cid:241) pierwsz(cid:241) i najwa(cid:276)niejsz(cid:241) rekomendacj(cid:241) dla ca(cid:228)ego te-
matu wspó(cid:228)bie(cid:276)no(cid:264)ci jest ksi(cid:241)(cid:276)ka C++ Concurrency in Action autorstwa Anthony’ego
Williamsa (patrz [Williams:C++Conc]).
Anthony jest jednym z kluczowych (cid:264)wiatowych ekspertów w tej dziedzinie,
powstanie tego rozdzia(cid:228)u bez jego wk(cid:228)adu nie by(cid:228)oby mo(cid:276)liwe. Nie tylko przejrza(cid:228)
niniejsz(cid:241) ksi(cid:241)(cid:276)k(cid:246), ale tak(cid:276)e dostarczy(cid:228) pierwszej implementacji standardowej biblio-
teki obs(cid:228)ugi wspó(cid:228)bie(cid:276)no(cid:264)ci (patrz [JustThread]), napisa(cid:228) kilka artyku(cid:228)ów oraz prze-
kaza(cid:228) cenne opinie. Wszystko to pomog(cid:228)o mi w przedstawieniu tego tematu —
mam nadziej(cid:246) w przydatny sposób. Dodatkowo jednak chcia(cid:228)bym podzi(cid:246)kowa(cid:232)
kilku innym ekspertom w dziedzinie wspó(cid:228)bie(cid:276)no(cid:264)ci, którzy pomogli mi w napi-
saniu tego rozdzia(cid:228)u: Hansowi Boehmowi, Scottowi Meyersowi, Bartoszowi Milew-
skiemu, Lawrence’owi Crowlowi i Peterowi Sommerladowi.
Niniejszy rozdzia(cid:228) jest zorganizowany w nast(cid:246)puj(cid:241)cy sposób:
(cid:120) Najpierw zaprezentuj(cid:246) ró(cid:276)ne sposoby uruchamiania wielu w(cid:241)tków. Po wpro-
wadzeniu w tematyk(cid:246) interfejsów zarówno wysokopoziomowych, jak i nisko-
poziomowych zaprezentuj(cid:246) szczegó(cid:228)owe informacje zwi(cid:241)zane z uruchamianiem
w(cid:241)tków.
(cid:120) W podrozdziale 18.4 zamieszczono szczegó(cid:228)owe omówienie problemu syn-
chronizowania w(cid:241)tków. G(cid:228)ównym problemem jest jednoczesny dost(cid:246)p do da-
nych.
(cid:120) Na koniec omówiono ró(cid:276)ne funkcje s(cid:228)u(cid:276)(cid:241)ce do synchronizacji w(cid:241)tków i jed-
noczesnego dost(cid:246)pu do danych:
(cid:120) Muteksy i blokady (patrz podrozdzia(cid:228) 18.5), z funkcj(cid:241) call_once() w(cid:228)(cid:241)cznie
(patrz punkt 18.5.3).
(cid:120) Zmienne warunkowe (patrz podrozdzia(cid:228) 18.6).
(cid:120) Atomowe typy danych (patrz podrozdzia(cid:228) 18.7).
Kup książkęPoleć książkę18.1. INTERFEJS WYSOKIEGO POZIOMU: ASYNC() I FUTURY
1003
18.1. Interfejs wysokiego poziomu: async()
i futury
Dla pocz(cid:241)tkuj(cid:241)cych najlepszym punktem wyj(cid:264)cia do uruchomienia programu z ob-
s(cid:228)ug(cid:241) wielu w(cid:241)tków jest skorzystanie z wysokopoziomowego interfejsu C++ biblio-
teki standardowej dostarczonego za po(cid:264)rednictwem wywo(cid:228)ania std::async() oraz
klasy std::future :
(cid:120) async() zapewnia interfejs umo(cid:276)liwiaj(cid:241)cy uruchomienie fragmentu funkcjonal-
no(cid:264)ci — obiektu wywo(cid:228)ywalnego (ang. callable object) (patrz podrozdzia(cid:228) 4.4)
— w tle, w osobnym w(cid:241)tku.
(cid:120) Klasa future pozwala czeka(cid:232) na zako(cid:254)czenie w(cid:241)tku i zapewnia dost(cid:246)p do
jego wyników: zwróconej warto(cid:264)ci lub wyj(cid:241)tku.
W tym rozdziale szczegó(cid:228)owo zaprezentowano ten interfejs wysokiego poziomu.
Temat rozszerzono o wprowadzenie do klasy std::shared_future , która pozwala
na oczekiwanie na zako(cid:254)czenie w(cid:241)tku i przetwarzanie jego wyników w wielu
miejscach.
18.1.1. Pierwszy przyk(cid:228)ad u(cid:276)ycia funkcji async() i futur
Przypu(cid:264)(cid:232)my, (cid:276)e musimy obliczy(cid:232) sum(cid:246) dwóch operandów zwracan(cid:241) przez dwa
wywo(cid:228)ania funkcji. Standardowy sposób zaprogramowania tej funkcjonalno(cid:264)ci
jest nast(cid:246)puj(cid:241)cy:
func1() + func2()
Oznacza to, (cid:276)e przetwarzanie operandów odbywa si(cid:246) sekwencyjnie. Program naj-
pierw wywo(cid:228)uje funkcj(cid:246) func1(), a nast(cid:246)pnie wywo(cid:228)uje funkcj(cid:246) func2() lub od-
wrotnie (zgodnie z regu(cid:228)ami j(cid:246)zyka kolejno(cid:264)(cid:232) jest niezdefiniowana). W obu przy-
padkach ca(cid:228)kowity czas przetwarzania wynosi: czas wykonywania funkcji func1()
plus czas wykonywania funkcji func2() plus czas wyliczenia sumy.
Obecnie, kiedy sprz(cid:246)t wieloprocesorowy jest dost(cid:246)pny niemal wsz(cid:246)dzie, mo(cid:276)e-
my wykonywa(cid:232) obliczenia wydajniej. Mo(cid:276)emy przynajmniej spróbowa(cid:232) uruchomi(cid:232)
funkcje func1() i func2() równolegle. Dzi(cid:246)ki temu ca(cid:228)kowity czas przetwarzania
b(cid:246)dzie równy sumie d(cid:228)u(cid:276)szego z czasów przetwarzania funkcji func1() i func2()
plus czas wyliczenia sumy.
Poni(cid:276)ej zaprezentowano pierwszy program realizuj(cid:241)cy obliczenia w taki sposób:
// concurrency/async1.cpp
#include future
#include thread
#include chrono
#include random
#include iostream
#include exception
using namespace std;
Kup książkęPoleć książkę1004
18. WSPÓ(cid:224)BIE(cid:297)NO(cid:285)(cid:251)
int doSomething (char c)
{
// generator liczb losowych (wykorzystuje c jako ziarno do uzyskania ró(cid:298)nych sekwencji)
std::default_random_engine dre(c);
std::uniform_int_distribution int id(10,1000);
// p(cid:266)tla wy(cid:286)wietlaj(cid:261)ca znak po up(cid:225)ywie losowego czasu
for (int i=0; i 10; ++i) {
this_thread::sleep_for(chrono::milliseconds(id(dre)));
cout.put(c).flush();
}
return c;
}
int func1 ()
{
return doSomething( . );
}
int func2 ()
{
return doSomething( + );
}
int main()
{
std::cout uruchomienie funkcji func1() w tle,
a funkcji func2() na pierwszym planie: std::endl;
// uruchomienie funkcji func1() asynchronicznie (teraz, pó(cid:296)niej lub nigdy):
std::future int result1(std::async(func1));
int result2 = func2(); // wywo(cid:225)anie funkcji func2() synchronicznie (tu i teraz)
// wy(cid:286)wietlenie wyniku (oczekiwanie na zako(cid:276)czenie funkcji func1() i dodanie jej wyniku do
zmiennej result2
int result = result1.get() + result2;
std::cout
wynik sumy func1()+func2(): result
std::endl;
}
W celu wizualizacji tego, co si(cid:246) dzieje, zasymulowali(cid:264)my z(cid:228)o(cid:276)one przetwarzanie
wewn(cid:241)trz funkcji func1() i func2() poprzez wywo(cid:228)anie funkcji doSomething(),
która od czasu do czasu wy(cid:264)wietla znak przekazany za pomoc(cid:241) argumentu1 i na
koniec zwraca warto(cid:264)(cid:232) przekazanego znaku jako warto(cid:264)(cid:232) int. „Od czasu do czasu”
zaimplementowano poprzez wykorzystanie generatora liczb losowych, który jest
odpowiedzialny za obliczenie interwa(cid:228)ów. Interwa(cid:228)y te s(cid:241) wykorzystywane w funkcji
std::this_thread::sleep_for() do wstrzymywania bie(cid:276)(cid:241)cego w(cid:241)tku (szcze-
gó(cid:228)owe informacje na temat liczb losowych mo(cid:276)na znale(cid:274)(cid:232) w podrozdziale 17.1,
natomiast szczegó(cid:228)owe informacje dotycz(cid:241)ce funkcji sleep_for()mo(cid:276)na znale(cid:274)(cid:232)
w punkcie 18.3.7). Zwró(cid:232)my uwag(cid:246), (cid:276)e aby generowane sekwencje liczb losowych
1 Generowanie wyj(cid:264)cia przez wspó(cid:228)bie(cid:276)ne w(cid:241)tki jest mo(cid:276)liwe, ale mo(cid:276)e skutkowa(cid:232)
przeplataniem si(cid:246) znaków z ró(cid:276)nych w(cid:241)tków (patrz podrozdzia(cid:228) 4.5).
Kup książkęPoleć książkę18.1. INTERFEJS WYSOKIEGO POZIOMU: ASYNC() I FUTURY
1005
by(cid:228)y ró(cid:276)ne, potrzebujemy unikatowego ziarna (ang. seed), które nale(cid:276)y przekaza(cid:232)
do konstruktora generatora liczb losowych (w tym przyk(cid:228)adzie w tej roli u(cid:276)yli-
(cid:264)my znaku c).
Zamiast wywo(cid:228)ania:
int result = func1() + func2();
wywo(cid:228)ujemy:
std::future int result1(std::async(func1));
int result2 = func2();
int result = result1.get() + result2;
Zatem najpierw próbujemy uruchomi(cid:232) funkcj(cid:246) func1() w tle, u(cid:276)ywaj(cid:241)c wywo(cid:228)ania
std::async(), a nast(cid:246)pnie przypisujemy wynik do obiektu klasy std::future:
std::future int result1(std::async(func1));
W powy(cid:276)szej instrukcji wywo(cid:228)anie async() próbuje natychmiast uruchomi(cid:232) przeka-
zan(cid:241) funkcjonalno(cid:264)(cid:232) asynchronicznie, w osobnym w(cid:241)tku. Tak wi(cid:246)c w idealnej sytu-
acji funkcja func1() rozpoczyna dzia(cid:228)anie w tym miejscu bez blokowania funkcji
main(). Zwracany obiekt futury jest konieczny z dwóch powodów:
1. Umo(cid:276)liwia dost(cid:246)p do „przysz(cid:228)ego” wyniku funkcji przekazanej do funkcji
async(). Ten wynik mo(cid:276)e by(cid:232) zwrócon(cid:241) warto(cid:264)ci(cid:241) albo wyj(cid:241)tkiem. Obiekt
futury jest wyspecjalizowany wed(cid:228)ug typu zwracanej warto(cid:264)ci uruchomionej
funkcjonalno(cid:264)ci. Je(cid:264)li uruchomiono tylko zadanie w tle, które niczego nie
zwraca, to musi to by(cid:232) obiekt std::future void .
2. Nale(cid:276)y zapewni(cid:232), aby przekazana funkcjonalno(cid:264)(cid:232) zosta(cid:228)a pr(cid:246)dzej lub pó(cid:274)niej
wywo(cid:228)ana. Zwró(cid:232)my uwag(cid:246), (cid:276)e napisa(cid:228)em: funkcja async() próbuje uruchomi(cid:232)
przekazan(cid:241) funkcjonalno(cid:264)(cid:232). Je(cid:264)li si(cid:246) to nie stanie, musimy wymusi(cid:232) na obiekcie
futury uruchomienie w chwili, gdy potrzebujemy wyniku lub gdy chcemy si(cid:246)
upewni(cid:232), (cid:276)e funkcjonalno(cid:264)(cid:232) zosta(cid:228)a wykonana. Z tego powodu obiekt futury
jest potrzebny nawet wtedy, gdy nie jeste(cid:264)my zainteresowani wynikiem funk-
cjonalno(cid:264)ci uruchomionej w tle.
Do wymiany danych pomi(cid:246)dzy miejscem, z którego uruchomiono funkcjonalno(cid:264)(cid:232)
i z którego ni(cid:241) zarz(cid:241)dzamy, a zwróconym obiektem futury s(cid:228)u(cid:276)y tzw. stan wspó(cid:228)-
dzielony (patrz podrozdzia(cid:228) 18.3).
Oczywi(cid:264)cie mo(cid:276)na równie(cid:276) (zazwyczaj tak b(cid:246)dziemy robi(cid:232)) u(cid:276)y(cid:232) s(cid:228)owa klu-
czowego auto do zadeklarowania futury (w tym przyk(cid:228)adzie chcia(cid:228)em to jawnie
zademonstrowa(cid:232)):
auto result1(std::async(func1));
Po drugie, funkcj(cid:246) func2() uruchamiamy na pierwszym planie. Jest to zwyczajne
synchroniczne wywo(cid:228)anie funkcji, dlatego program blokuje si(cid:246) w tym miejscu:
int result2 = func2();
Zatem je(cid:264)li funkcja func1() zosta(cid:228)a pomy(cid:264)lnie uruchomiona przez funkcj(cid:246) async()
i jeszcze si(cid:246) nie zako(cid:254)czy(cid:228)a, to funkcje func1() i func2() s(cid:241) teraz uruchomione jed-
nocze(cid:264)nie.
Kup książkęPoleć książkę1006
18. WSPÓ(cid:224)BIE(cid:297)NO(cid:285)(cid:251)
W trzeciej kolejno(cid:264)ci przetwarzamy sum(cid:246). To jest moment, kiedy potrzebujemy
wyniku funkcji func1(). Aby go uzyska(cid:232), wywo(cid:228)ujemy funkcj(cid:246) get() w odniesieniu
do zwróconego obiektu futury:
int result = result1.get() + result2;
W momencie wywo(cid:228)ywania funkcji get() mo(cid:276)e zachodzi(cid:232) jedna z trzech sytuacji:
1. Je(cid:264)li funkcja func1() zosta(cid:228)a uruchomiona za pomoc(cid:241) funkcji async() w od-
dzielnym w(cid:241)tku i ju(cid:276) si(cid:246) zako(cid:254)czy(cid:228)a, to natychmiast uzyskamy jej wynik.
2. Je(cid:264)li funkcja func1() zosta(cid:228)a uruchomiona, ale jeszcze si(cid:246) nie zako(cid:254)czy(cid:228)a, to
funkcja get() blokuje si(cid:246) i czeka na jej zako(cid:254)czenie. Na koniec zwraca wynik.
3. Je(cid:264)li funkcja func1() jeszcze nie zosta(cid:228)a uruchomiona, b(cid:246)dzie zmuszona do
natychmiastowego uruchomienia i tak jak dla synchronicznego wywo(cid:228)ania
funkcji get() zablokuje si(cid:246) w oczekiwaniu na wynik.
To zachowanie jest bardzo wa(cid:276)ne, poniewa(cid:276) zapewnia ono dzia(cid:228)anie programu
tak(cid:276)e w (cid:264)rodowisku jednow(cid:241)tkowym oraz w przypadku, kiedy funkcja async()
z jakiego(cid:264) powodu nie mog(cid:228)a uruchomi(cid:232) nowego w(cid:241)tku.
Wywo(cid:228)anie funkcji async() nie gwarantuje, (cid:276)e przekazana do niej funkcjo-
nalno(cid:264)(cid:232) zostanie rozpocz(cid:246)ta i si(cid:246) zako(cid:254)czy. Je(cid:264)li w(cid:241)tek jest dost(cid:246)pny, to zostanie
uruchomiony, ale je(cid:264)li nie — ze wzgl(cid:246)du na to, (cid:276)e okre(cid:264)lone (cid:264)rodowisko nie ob-
s(cid:228)uguje wielow(cid:241)tkowo(cid:264)ci lub nie ma dost(cid:246)pnych wi(cid:246)cej w(cid:241)tków — wtedy wywo-
(cid:228)anie zostanie odroczone do momentu, kiedy jawnie stwierdzimy, (cid:276)e potrzebu-
jemy jego wyniku (poprzez wywo(cid:228)anie get()), lub po prostu kiedy b(cid:246)dziemy
chcieli, aby okre(cid:264)lona funkcjonalno(cid:264)(cid:232) zosta(cid:228)a wykonana (poprzez wywo(cid:228)anie wait()
— patrz punkt 18.1.1).
A zatem kombinacja instrukcji:
std::future int result1(std::async(func1));
oraz
result1.get()
pozwala zoptymalizowa(cid:232) program w taki sposób, (cid:276)e je(cid:264)li to b(cid:246)dzie mo(cid:276)liwe, funkcja
func1() b(cid:246)dzie dzia(cid:228)a(cid:232) wspó(cid:228)bie(cid:276)nie w czasie, gdy s(cid:241) przetwarzane nast(cid:246)pne in-
strukcje w g(cid:228)ównym w(cid:241)tku. Je(cid:264)li nie ma mo(cid:276)liwo(cid:264)ci jej wspó(cid:228)bie(cid:276)nego urucho-
mienia, b(cid:246)dzie wywo(cid:228)ana sekwencyjnie w momencie, gdy zostanie wywo(cid:228)ana
funkcja get(). Oznacza to, (cid:276)e w ka(cid:276)dym przypadku uzyskujemy gwarancj(cid:246), (cid:276)e
po wywo(cid:228)aniu get() funkcja func1() b(cid:246)dzie wywo(cid:228)ana asynchronicznie lub syn-
chronicznie.
W zwi(cid:241)zku z tym ten program mo(cid:276)e generowa(cid:232) dwa rodzaje wyników. Je(cid:264)li
funkcja async() mo(cid:276)e pomy(cid:264)lnie uruchomi(cid:232) funkcj(cid:246) func1(), wynik mo(cid:276)e mie(cid:232)
nast(cid:246)puj(cid:241)c(cid:241) posta(cid:232):
uruchomienie funkcji func1() w tle, a funkcji func2() na pierwszym planie:
++..++++.++.+.+.....
wynik sumy func1()+func2(): 89
Kup książkęPoleć książkę18.1. INTERFEJS WYSOKIEGO POZIOMU: ASYNC() I FUTURY
1007
Je(cid:264)li funkcja async() nie mog(cid:228)a uruchomi(cid:232) funkcji func1(), funkcja ta zostanie uru-
chomiona po funkcji func2(), w momencie wywo(cid:228)ania funkcji get(). W zwi(cid:241)zku
z tym program b(cid:246)dzie mia(cid:228) nast(cid:246)puj(cid:241)cy wynik:
uruchomienie funkcji func1() w tle, a funkcji func2() na pierwszym planie:
++++++++++..........
wynik sumy func1()+func2(): 89
Tak wi(cid:246)c na podstawie pierwszego przyk(cid:228)adu mo(cid:276)emy zdefiniowa(cid:232) ogólny spo-
sób na to, by program dzia(cid:228)a(cid:228) szybciej: mo(cid:276)emy zmodyfikowa(cid:232) program w taki
sposób, aby móg(cid:228) korzysta(cid:232) ze wspó(cid:228)bie(cid:276)no(cid:264)ci, je(cid:264)li jest dost(cid:246)pna na platformie,
na której uruchomiono program, ale by móg(cid:228) dzia(cid:228)a(cid:232) tak(cid:276)e w (cid:264)rodowiskach jed-
now(cid:241)tkowych. W tym celu nale(cid:276)y wykona(cid:232) nast(cid:246)puj(cid:241)ce czynno(cid:264)ci:
(cid:120) umie(cid:264)ci(cid:232) w programie dyrektyw(cid:246) #include future ;
(cid:120) przekaza(cid:232) funkcjonalno(cid:264)(cid:232), które mo(cid:276)e by(cid:232) uruchomiona samodzielnie jako
wywo(cid:228)ywalny obiekt, do funkcji std::async();
(cid:120) przypisa(cid:232) wynik do obiektu future ZwracanyTyp ;
(cid:120) wywo(cid:228)a(cid:232) funkcj(cid:246) get() na rzecz obiektu future , kiedy b(cid:246)dzie potrzebny
nam wynik lub kiedy b(cid:246)dziemy chcieli mie(cid:232) pewno(cid:264)(cid:232), (cid:276)e uruchomiona
funkcjonalno(cid:264)(cid:232) si(cid:246) zako(cid:254)czy(cid:228)a.
Nale(cid:276)y jednak pami(cid:246)ta(cid:232), (cid:276)e dotyczy to tylko takiej sytuacji, kiedy nie wyst(cid:246)puje
wy(cid:264)cig o dane, co oznacza, (cid:276)e dwa w(cid:241)tki równocze(cid:264)nie korzystaj(cid:241) z tych samych da-
nych, co mo(cid:276)e by(cid:232) przyczyn(cid:241) niezdefiniowanego zachowania (patrz punkt 18.4.1).
Zwró(cid:232)my uwag(cid:246), (cid:276)e bez wywo(cid:228)ania funkcji get() nie mamy gwarancji, czy
funkcja func1() kiedykolwiek b(cid:246)dzie wywo(cid:228)ana. Zgodnie z tym, co napisano wcze-
(cid:264)niej, je(cid:264)li funkcja async() nie mo(cid:276)e uruchomi(cid:232) przekazanej funkcjonalno(cid:264)ci na-
tychmiast, odracza wywo(cid:228)anie do chwili jawnego za(cid:276)(cid:241)dania wyniku przekazanej
funkcjonalno(cid:264)ci za pomoc(cid:241) funkcji get() (lub wait()). Jednak bez takiego (cid:276)(cid:241)dania
zako(cid:254)czenie dzia(cid:228)ania funkcji main() spowoduje zako(cid:254)czenie programu nawet
bez wywo(cid:228)ania w(cid:241)tku dzia(cid:228)aj(cid:241)cego w tle.
Zwró(cid:232)my tak(cid:276)e uwag(cid:246), (cid:276)e musimy zadba(cid:232) o to, aby (cid:276)(cid:241)danie wyniku funk-
cjonalno(cid:264)ci rozpocz(cid:246)tej za pomoc(cid:241) funkcji async() nie nast(cid:241)pi(cid:228)o wcze(cid:264)niej, ni(cid:276) to
konieczne. Na przyk(cid:228)ad poni(cid:276)sza „optymalizacja” prawdopodobnie nie przyniesie
spodziewanego efektu:
std::future int result1(std::async(func1));
int result = func2() + result1.get(); // wywo(cid:225)anie funkcji func2() mo(cid:298)e nast(cid:261)pi(cid:252) po
zako(cid:276)czeniu dzia(cid:225)ania funkcji func1()
Poniewa(cid:276) kolejno(cid:264)(cid:232) wykonywania dzia(cid:228)a(cid:254) po prawej stronie drugiej instrukcji
jest niezdefiniowana, wywo(cid:228)anie result1.get() mo(cid:276)e nast(cid:241)pi(cid:232) przed wywo(cid:228)aniem
func2(), a zatem b(cid:246)dzie to ponownie przetwarzanie sekwencyjne.
Aby uzyska(cid:232) najlepszy efekt, ogólnie rzecz bior(cid:241)c, dystans pomi(cid:246)dzy wywo-
(cid:228)aniami async() i get() powinien by(cid:232) jak najwi(cid:246)kszy. Mo(cid:276)na równie(cid:276) zastosowa(cid:232)
warunki okre(cid:264)lone w [N3194:Futures]: Wczesne wywo(cid:228)anie i pó(cid:274)ne zwrócenie wyniku.
Je(cid:264)li operacja przekazana do funkcji async() nie zwraca (cid:276)adnej warto(cid:264)ci, funkcja
async() zwraca obiekt future void , który jest cz(cid:246)(cid:264)ciow(cid:241) specjalizacj(cid:241) obiektu
future . W takim przypadku wywo(cid:228)anie get() nie zwraca niczego:
Kup książkęPoleć książkę1008
18. WSPÓ(cid:224)BIE(cid:297)NO(cid:285)(cid:251)
std::future void f(std::async(func)); // próba asynchronicznego wywo(cid:225)ania funkcji
...
f.get(); // oczekiwanie na zako(cid:276)czenie funkcji (zwraca void)
Na koniec zwró(cid:232)my uwag(cid:246), (cid:276)e obiekt przekazany do funkcji async() mo(cid:276)e by(cid:232)
obiektem wywo(cid:228)ywalnym dowolnego typu: funkcj(cid:241), funkcj(cid:241) sk(cid:228)adow(cid:241), obiektem funk-
cyjnym lub wyra(cid:276)eniem lambda (patrz podrozdzia(cid:228) 4.4). Zatem mo(cid:276)emy równie(cid:276)
przekaza(cid:232) funkcjonalno(cid:264)(cid:232) inline jako wyra(cid:276)enie lambda. Funkcjonalno(cid:264)(cid:232) ta b(cid:246)dzie
dzia(cid:228)a(cid:228)a we w(cid:228)asnym w(cid:241)tku (patrz punkt 3.1.10):
std::async([]{ ... }) // próba uruchomienia kodu ... asynchronicznie
Strategie uruchamiania
Mo(cid:276)emy zapobiec odroczeniu uruchomienia funkcjonalno(cid:264)ci przekazanej do
funkcji async() poprzez jawne przekazanie strategii uruchamiania2. W ten sposób
mo(cid:276)emy nakaza(cid:232) funkcji async(), aby ta uruchomi(cid:228)a przekazan(cid:241) funkcjonalno(cid:264)(cid:232)
dok(cid:228)adnie w momencie wywo(cid:228)ania:
// wymuszenie asynchronicznego uruchomienia funkcji func1(); w przypadku niepowodzenia zg(cid:225)aszany
// jest wyj(cid:261)tek std::system_error
std::future long result1= std::async(std::launch::async, func1);
Je(cid:264)li wywo(cid:228)anie asynchroniczne nie jest mo(cid:276)liwe w tym miejscu, program zg(cid:228)osi
wyj(cid:241)tek std::system_error (patrz punkt 4.3.1) z kodem b(cid:228)(cid:246)du resource_unavailable_
(cid:180)try_again. Jest to odpowiednik kodu b(cid:228)(cid:246)du POSIX errno EAGAIN (patrz punkt
4.3.2).
Przy u(cid:276)yciu strategii uruchamiania async nie musimy wywo(cid:228)ywa(cid:232) funkcji get(),
poniewa(cid:276) je(cid:264)li czas (cid:276)ycia zwróconej futury dobiegnie ko(cid:254)ca, program zaczeka na
zako(cid:254)czenie dzia(cid:228)ania funkcji func1(). Je(cid:264)li wi(cid:246)c nie wywo(cid:228)amy funkcji get(), to
po opuszczeniu zakresu obiektu futury (tutaj b(cid:246)dzie nim koniec funkcji main())
program b(cid:246)dzie czeka(cid:228) na zako(cid:254)czenie zadania dzia(cid:228)aj(cid:241)cego w tle. Niemniej jednak
wywo(cid:228)anie funkcji get() zanim program zako(cid:254)czy dzia(cid:228)anie, sprawia, (cid:276)e zacho-
wanie kodu staje si(cid:246) czytelniejsze.
Je(cid:264)li nigdzie nie przypiszemy wyniku std::async(std::launch::async,...),
obiekt wywo(cid:228)uj(cid:241)cy zablokuje si(cid:246) do czasu zako(cid:254)czenia przekazanej funkcjonalno(cid:264)ci.
W takim przypadku b(cid:246)dzie to równoznaczne z wywo(cid:228)aniem synchronicznym3.
W podobny sposób mo(cid:276)emy wymusi(cid:232) odroczone wywo(cid:228)anie poprzez prze-
kazanie do funkcji async() strategii uruchamiania std::launch:deferred. Poni(cid:276)sza
sekwencja instrukcji spowoduje odroczenie funkcji func1() do czasu wywo(cid:228)ania
funkcji get() na rzecz obiektu f:
2 Strategia uruchamiania jest typem wyliczeniowym o okre(cid:264)lonym zasi(cid:246)gu, dlatego trzeba
kwalifikowa(cid:232) warto(cid:264)ci (enumeratory) za pomoc(cid:241) prefiksu std::launch lub launch (patrz
punkt 3.1.13).
3 Nale(cid:276)y zauwa(cid:276)y(cid:232), (cid:276)e w komitecie standaryzacyjnym by(cid:228)y kontrowersje dotycz(cid:241)ce
sposobu interpretacji tych s(cid:228)ów w przypadku, gdy wynik funkcji async() nie jest u(cid:276)ywa-
ny. Taki by(cid:228) rezultat dyskusji i takie powinno by(cid:232) zachowanie programu we wszystkich
implementacjach.
Kup książkęPoleć książkę18.1. INTERFEJS WYSOKIEGO POZIOMU: ASYNC() I FUTURY
1009
std::future ... f(std::async(std::launch::deferred,
func1)); // odroczenie funkcji func1 do czasu wywo(cid:225)ania get()
W tym przypadku mamy gwarancj(cid:246), (cid:276)e funkcja func1() nie zostanie wywo(cid:228)ana bez
wywo(cid:228)ania get() (lub wait()).
Ta strategia umo(cid:276)liwia tzw. leniwe warto(cid:264)ciowanie (ang. lazy evaluation). Na
przyk(cid:228)ad4:
auto f1 = std::async( std::launch::deferred, task1 );
auto f2 = std::async( std::launch::deferred, task2 );
...
auto val = thisOrThatIsTheCase() ? f1.get() : f2.get();
Ponadto jawne (cid:276)(cid:241)danie strategii uruchamiania deferred mo(cid:276)e pomóc w zasy-
mulowaniu dzia(cid:228)ania funkcji async() w (cid:264)rodowisku jednow(cid:241)tkowym. U(cid:228)atwia
równie(cid:276) debugowanie (o ile nie zachodz(cid:241) warunki wy(cid:264)cigu).
Obs(cid:228)uga wyj(cid:241)tków
Dotychczas omawiali(cid:264)my tylko taki przypadek, kiedy w(cid:241)tki i zadania wykonywane
w tle ko(cid:254)czy(cid:228)y si(cid:246) pomy(cid:264)lnie. Jednak co si(cid:246) zdarzy, kiedy wyst(cid:241)pi wyj(cid:241)tek?
Dobra wiadomo(cid:264)(cid:232) jest taka, (cid:276)e nic specjalnego. Wywo(cid:228)anie funkcji get() dla
futur obs(cid:228)uguje równie(cid:276) wyj(cid:241)tki. Je(cid:264)li nast(cid:241)pi wywo(cid:228)anie get(), a operacja w tle
zostanie przerwana przez wyj(cid:241)tek, który nie zosta(cid:228) obs(cid:228)u(cid:276)ony wewn(cid:241)trz w(cid:241)tku, to
ten wyj(cid:241)tek b(cid:246)dzie propagowany dalej. W efekcie w celu obs(cid:228)ugi wyj(cid:241)tków operacji
wykonywanych w tle nale(cid:276)y post(cid:246)powa(cid:232) z funkcj(cid:241) get() w taki sam sposób,
w jaki post(cid:241)piliby(cid:264)my, gdyby operacja by(cid:228)a uruchomiona synchronicznie.
Na przyk(cid:228)ad spróbujmy uruchomi(cid:232) zadanie w tle z niesko(cid:254)czon(cid:241) p(cid:246)tl(cid:241) alokuj(cid:241)-
c(cid:241) pami(cid:246)(cid:232) w celu wstawienia nowego elementu listy5:
// concurrency/async2.cpp
#include future
#include list
#include iostream
#include exception
using namespace std;
void task1()
{
// niesko(cid:276)czone wstawianie elementu i alokacja pami(cid:266)ci
// - pr(cid:266)dzej czy pó(cid:296)niej spowoduje zg(cid:225)oszenie wyj(cid:261)tku
// - UWAGA: to jest z(cid:225)a praktyka
list int v;
while (true) {
for (int i=0; i 1000000; ++i) {
v.push_back(i);
4 Dzi(cid:246)kujemy Lawrence’owi Crowlowi za t(cid:246) uwag(cid:246) oraz za dostarczenie przyk(cid:228)adu.
5 Próba zu(cid:276)ywania pami(cid:246)ci do momentu, a(cid:276) wyst(cid:241)pi wyj(cid:241)tek, jest oczywi(cid:264)cie z(cid:228)(cid:241) praktyk(cid:241).
W (cid:264)rodowiskach niektórych systemów operacyjnych mo(cid:276)e to spowodowa(cid:232) problemy.
W zwi(cid:241)zku z tym uruchamiaj(cid:241)c ten przyk(cid:228)ad, nale(cid:276)y zachowa(cid:232) ostro(cid:276)no(cid:264)(cid:232).
Kup książkęPoleć książkę1010
18. WSPÓ(cid:224)BIE(cid:297)NO(cid:285)(cid:251)
}
cout.put( . ).flush();
}
}
int main()
{
cout uruchomienie 2 zada(cid:241) endl;
cout - task1: przetwarzanie niesko(cid:241)czonej p(cid:218)tli zu(cid:285)ywaj(cid:200)cej pami(cid:218)(cid:202) endl;
cout - task2: oczekiwanie na zwrócenie sterowania, a nast(cid:218)pnie na zako(cid:241)czenie
zadania task1 endl;
auto f1 = async(task1); // uruchomienie zadania task1() asynchronicznie (teraz, pó(cid:296)niej lub nigdy)
cin.get(); // czytanie znaku (podobnie jak getchar())
cout
oczekiwanie na zako(cid:241)czenie zadania task1: endl;
try {
f1.get(); // oczekiwanie na zako(cid:276)czenie zadania task1() (lub zg(cid:225)oszenie wyj(cid:261)tku)
}
catch (const exception e) {
cerr WYJ(cid:107)TEK: e.what() endl;
}
}
Pr(cid:246)dzej czy pó(cid:274)niej niesko(cid:254)czona p(cid:246)tla spowoduje zg(cid:228)oszenie wyj(cid:241)tku (prawdopo-
dobnie b(cid:246)dzie to wyj(cid:241)tek bad_alloc — patrz punkt 4.3.1). Ten wyj(cid:241)tek spowoduje
zako(cid:254)czenie w(cid:241)tku, poniewa(cid:276) nie jest nigdzie przechwycony. Ten stan b(cid:246)dzie zapi-
sany w obiekcie futury do czasu wywo(cid:228)ania funkcji get(). Wywo(cid:228)anie funkcji
get() spowoduje dalsz(cid:241) propagacj(cid:246) wyj(cid:241)tku wewn(cid:241)trz funkcji main().
Mo(cid:276)emy teraz podsumowa(cid:232) interfejs funkcji async() i obiektów futur w na-
st(cid:246)puj(cid:241)cy sposób: funkcja async() daje (cid:264)rodowisku programistycznemu szans(cid:246)
na równoleg(cid:228)e uruchomienie oblicze(cid:254), których wyniki b(cid:246)d(cid:241) wykorzystane pó(cid:274)-
niej (w momencie wywo(cid:228)ania funkcji get()). Inaczej mówi(cid:241)c, je(cid:264)li mamy pewn(cid:241)
niezale(cid:276)n(cid:241) funkcjonalno(cid:264)(cid:232) f, to mo(cid:276)emy skorzysta(cid:232) z równoleg(cid:228)ego przetwarza-
nia, je(cid:264)li jest ono mo(cid:276)liwe, poprzez przekazanie f do funkcji async() w chwili,
gdy mamy wszystko, co jest potrzebne do uruchomienia tej funkcjonalno(cid:264)ci. Na-
st(cid:246)pnie w miejscu, gdzie jest potrzebny wynik funkcjonalno(cid:264)ci f, nale(cid:276)y wstawi(cid:232)
wywo(cid:228)anie funkcji get() w odniesieniu do futury zwróconej przez funkcj(cid:246)
async(). W ten sposób uzyskujemy ten sam wynik, ale mamy szans(cid:246) na lepsz(cid:241)
wydajno(cid:264)(cid:232), poniewa(cid:276) funkcjonalno(cid:264)(cid:232) f mo(cid:276)e dzia(cid:228)a(cid:232) wspó(cid:228)bie(cid:276)nie, zanim b(cid:246)dzie
potrzebny jej wynik.
Oczekiwanie i odpytywanie
Funkcj(cid:246) get() w odniesieniu do obiektu future mo(cid:276)na wywo(cid:228)a(cid:232) tylko raz. Po
wywo(cid:228)aniu funkcji get() futura jest niewa(cid:276)na. Mo(cid:276)na to sprawdzi(cid:232) jedynie poprzez
wywo(cid:228)anie funkcji valid() w odniesieniu do futury. Ka(cid:276)de wywo(cid:228)anie inne ni(cid:276)
niszcz(cid:241)ce obiekt spowoduje niezdefiniowane zachowanie (szczegó(cid:228)owe informacje
mo(cid:276)na znale(cid:274)(cid:232) w punkcie 18.3.2).
Kup książkęPoleć książkę18.1. INTERFEJS WYSOKIEGO POZIOMU: ASYNC() I FUTURY
1011
Futury zapewniaj(cid:241) równie(cid:276) interfejs pozwalaj(cid:241)cy na oczekiwanie na zako(cid:254)-
czenie operacji dzia(cid:228)aj(cid:241)cej w tle bez przetwarzania jej wyniku. Interfejs ten mo(cid:276)e
by(cid:232) wywo(cid:228)ywany wi(cid:246)cej ni(cid:276) jeden raz. Aby ograniczy(cid:232) czas oczekiwania, mo(cid:276)na
do niego przekaza(cid:232) czas trwania lub punkt w czasie.
Samo wywo(cid:228)anie funkcji wait() wymusza uruchomienie w(cid:241)tku reprezento-
wanego przez futur(cid:246) i oczekiwanie na zako(cid:254)czenie operacji w tle:
std::future ... f(std::async(func)); // próba asynchronicznego wywo(cid:225)ania funkcji
...
f.wait(); // oczekiwanie na zako(cid:276)czenie funkcji (mo(cid:298)e uruchomi(cid:252) zadanie w tle)
Istniej(cid:241) dwie inne odmiany funkcji wait(), które mo(cid:276)na wywo(cid:228)ywa(cid:232) w odniesieniu
do futur. Funkcje te nie wymuszaj(cid:241) uruchomienia w(cid:241)tku, je(cid:264)li nie zosta(cid:228) on urucho-
miony wcze(cid:264)niej:
1. Funkcja wait_for(), do której przekazujemy czas oczekiwania, pozwala czeka(cid:232)
na asynchroniczne uruchomienie operacji przez ograniczony czas:
std::future ... f(std::async(func)); // próba asynchronicznego wywo(cid:225)ania funkcji
...
f.wait_for(std::chrono::seconds(10)); // oczekiwanie na funkcj(cid:266) func przez co najwy(cid:298)ej 10
sekund
2. Funkcja wait_until() pozwala czeka(cid:232) do okre(cid:264)lonego punktu w czasie:
std::future ... f(std::async(func)); // próba asynchronicznego wywo(cid:225)ania funkcji
...
f.wait_until(std::system_clock::now()+std::chrono::minutes(1));
Zarówno funkcja wait_for(), jak i wait_until() zwracaj(cid:241) jeden z poni(cid:276)szych
wyników:
(cid:120) std::future_status::deferred (cid:127) je(cid:264)li funkcja async() odroczy(cid:228)a operacj(cid:246)
i (cid:276)adne z wywo(cid:228)a(cid:254) wait() lub get() jeszcze nie wymusi(cid:228)o jej startu (w tym
przypadku obie funkcje natychmiast zwracaj(cid:241) sterowanie);
(cid:120) std::future_status::timeout (cid:127) je(cid:264)li operacja zosta(cid:228)a uruchomiona asyn-
chronicznie, ale jeszcze si(cid:246) nie zako(cid:254)czy(cid:228)a (je(cid:264)li oczekiwanie zako(cid:254)czy(cid:228)o si(cid:246)
ze wzgl(cid:246)du na up(cid:228)yw przekazanego limitu czasu);
(cid:120) std::future_status::ready (cid:127) je(cid:264)li operacja zako(cid:254)czy(cid:228)a si(cid:246).
Wykorzystanie funkcji wait_for() lub wait_until() umo(cid:276)liwia tzw. spekulacyjne
uruchomienie programu. Na przyk(cid:228)ad rozwa(cid:276)my scenariusz, w którym musimy uzy-
ska(cid:232) wynik oblicze(cid:254) w okre(cid:264)lonym czasie i chcieliby(cid:264)my mie(cid:232) dok(cid:228)adny wynik6:
int quickComputation(); // przetwarzanie wyniku szybkie i niedok(cid:225)adne
int accurateComputation(); // przetwarzanie wyniku dok(cid:225)adne, ale wolne
std::future int f; //zadeklarowane na zewn(cid:261)trz ze wzgl(cid:266)du na to, (cid:298)e czas (cid:298)ycia funkcji
accurateComputation()
// mo(cid:298)e przekroczy(cid:252) czas (cid:298)ycia funkcji bestResultInTime()
int bestResultInTime()
{
// zdefiniowanie przedzia(cid:225)u czasowego do uzyskania odpowiedzi:
auto tp = std::chrono::system_clock::now() + std::chrono::minutes(1);
6 Dzi(cid:246)kujemy Lawrence’owi Crowlowi za te informacje oraz za dostarczenie przyk(cid:228)adu.
Kup książkęPoleć książkę1012
18. WSPÓ(cid:224)BIE(cid:297)NO(cid:285)(cid:
Pobierz darmowy fragment (pdf)