• Nie Znaleziono Wyników

Programowanie współbieżne w C++

N/A
N/A
Protected

Academic year: 2022

Share "Programowanie współbieżne w C++"

Copied!
33
0
0

Pełen tekst

(1)

Współbieżność

Programowanie współbieżne w C++ — Wątki

(wersja wstępna)

Bogdan Kreczmer

bogdan.kreczmer@pwr.edu.pl

Katedra Cybernetyki i Robotyki Wydziału Elektroniki Politechnika Wrocławska

Kurs: Zaawansowane metody programowania

Copyright c 2019 Bogdan Kreczmer

Niniejszy dokument zawiera materiały do wykładu dotyczącego programowania obiektowego. Jest on udostępniony pod warunkiem wykorzystania wyłącznie do własnych prywatnych potrzeb i może on być kopiowany

(2)

Niniejsza prezentacja została wykonana przy użyciu sys- temu składu PDFL A TEX oraz stylu beamer, którego au- torem jest Till Tantau.

Strona domowa projektu Beamer:

http://latex-beamer.sourceforge.net

(3)

Współbieżność

1 Współbieżność

Rodzaje współbieżności

(4)

Rodzaje współbieżności

Programowa współbieżność – (software concurrency) określana jest również jako współbieżność wirtualna (virtual concurrency)

Może być realizowany, gdy komputer posiada jeden procesor z pojedynczym rdzeniem.

Sprzętowa współbieżność – (hardware concurrency) określana jest również jako współbieżność prawdziwa (true concurrency)

Możliwa jest tylko w przypadku komputerów wieloprocesorowych lub z

pojedynczym procesorem wielordzeniowym. O ilości współbieżnych

procesów decyduje liczba niezależnych jednostek obliczeniowych (łączna

liczba rdzeni we wszystkich procesorach).

(5)

Współbieżność Rodzaje współbieżności

Rodzaje współbieżności

System wieloprocesorowy/rdzeniowy nie wyklucza programowej

realizacji współbieżności. Zazwyczaj procesów, które muszą

jednocześnie działać, jest znacznie więcej niż dostępnych rdzeni.

(6)

Współbieżne procesy vs współbieżne wątki

Przełączanie się między zadaniami, rozumianymi jako osobne procesy, wymaga zmiany całego kontekstu zadania. Dotyczy on zarówno stanu wykonanywania programu jak też danych.

Przełączanie się między wątkami wymaga zmiany jedynie kontekstu związanego z realizacją programu.

Należy pamiętać

Przełączanie między wątkami, aczkolwiek jest szybsze niż przełączanie między osobnymi procesami, wciąż jednak generuje dodatkowy narzut.

Należy więc ograniczyć liczbę wątków do niezbędnej ilości.

(7)

Współbieżność Rodzaje współbieżności

Komunikacja między procesami

Procesy między sobą mogą komunikować się wykorzystując

mechanizmy, które dostarcza system operacyjny.

(8)

Komunikacja między procesami

Komunikacja ta może być realizowana na różne sposoby, np.

poprzez gniazda (ang. sockets), pamięć dzieloną, czy też

przekierowanie wyjść i wejść standardowych.

(9)

Współbieżność Rodzaje współbieżności

Komunikacja między wątkami

W procesach wielowątkowych komunikacja wewnątrz procesu

między poszczególnymi wątkami odbywa się poprzez obszar pamięci

procesu.

(10)

Komunikacja między wątkami

Procesy wielowątkowe mogą pracować niezależnie i zupełnie

odizolowane od innych.

(11)

Współbieżność Rodzaje współbieżności

Komunikacja między wątkami

Mogą jednak też komunikować się ze sobą wykorzystując standardowe mechanizmy dostarczane przez system operacyjny.

Należy jednak zwrócić uwagę, że w takim przypadku komunikacja

zachodzi między wybranymi wątkami.

(12)

Komunikacja między wątkami

Komunikacja może zachodzić jednocześnie między różnymi

wątkami dwóch procesów i w każdym z kanałów komunikacyjnych

mogą być wykorzystywane różne mechanizmy.

(13)

Współbieżność Rodzaje współbieżności

Komunikacja między wątkami

Możliwa jest też komunikacja kilku wątków jednego procesu z

pojedynczym wątkiem innego procesu.

(14)

Ilość wątków

Maksymalna ilość sprzętowych wątków

std::thread::hardware concurrency()

Ta metoda statyczna może zwrócić zero, jeśli na danym sprzęcie nie jest w stanie poprawnie wykryć ilości procesorów lub rdzeni.

#i n c l u d e <i o s t r e a m >

#i n c l u d e <t h r e a d>

i n t main ( ) {

s t d : : c o u t << s t d : : t h r e a d : : h a r d w a r e c o n c u r r e n c y ( ) << s t d : : e n d l ; }

(15)

Współbieżność Rodzaje współbieżności

Najprostszy wątek w C

#i n c l u d e <s t d i o . h>

#i n c l u d e <p t h r e a d . h>

v o i d ∗ s i m p l e T h r e a d ( v o i d ∗pArg ) {

p r i n t f ( ” . . . Komunikat watku\n ” ) ; }

i n t main ( ) {

p t h r e a d t Thr ;

v o i d ∗pRes ;

i f ( p t h r e a d c r e a t e (&Thr , NULL , s i m p l e T h r e a d , NULL ) < 0 ) { r e t u r n 1 ;

}

p t h r e a d j o i n ( Thr ,& pRes ) ; }

i n t p t h r e a d c r e a t e ( p t h r e a d t ∗ t h r e a d , c o n s t p t h r e a d a t t r t ∗ a t t r , v o i d ∗(∗ s t a r t r o u t i n e ) ( v o i d ∗ ) ,

v o i d ∗ a r g

) ;

Za działanie wątku odpowiada funkcja simpleThread, której wskaźnik jest przekazywany do funkcji tworzącej nowy wątek.

Funkcja simpleThread staje się rodzajem funkcji main dla nowego wątku.

Wywołanie funkcji pthread create powoduje utworzenie wątku i uruchomienie w nim funkcji simpleThread.

Wywołanie funkcji pthread join jest konieczny, aby główny wątek procesu zaczekał na zakończenie wątku wykonywanego przez funkcję simpleThread.

Prototyp funkcji

(16)

Współbieżność Rodzaje współbieżności

Najprostszy wątek w C

#i n c l u d e <s t d i o . h>

#i n c l u d e <p t h r e a d . h>

v o i d ∗ s i m p l e T h r e a d ( v o i d ∗pArg ) {

p r i n t f ( ” . . . Komunikat watku\n ” ) ; }

i n t main ( ) {

p t h r e a d t Thr ;

v o i d ∗pRes ;

i f ( p t h r e a d c r e a t e (&Thr , NULL , s i m p l e T h r e a d , NULL ) < 0 ) { r e t u r n 1 ;

}

p t h r e a d j o i n ( Thr ,& pRes ) ; }

i n t p t h r e a d c r e a t e ( p t h r e a d t ∗ t h r e a d , c o n s t p t h r e a d a t t r t ∗ a t t r , v o i d ∗(∗ s t a r t r o u t i n e ) ( v o i d ∗ ) ,

v o i d ∗ a r g

) ;

Za działanie wątku odpowiada funkcja simpleThread, której wskaźnik jest przekazywany do funkcji tworzącej nowy wątek.

Funkcja simpleThread staje się rodzajem funkcji main dla nowego wątku.

Wywołanie funkcji pthread create powoduje utworzenie wątku i uruchomienie w nim funkcji simpleThread.

zaczekał na zakończenie wątku wykonywanego przez funkcję simpleThread.

Prototyp funkcji pthread create

(17)

Współbieżność Rodzaje współbieżności

Najprostszy wątek w C

#i n c l u d e <s t d i o . h>

#i n c l u d e <p t h r e a d . h>

v o i d ∗ s i m p l e T h r e a d ( v o i d ∗pArg ) {

p r i n t f ( ” . . . Komunikat watku\n ” ) ; }

i n t main ( ) {

p t h r e a d t Thr ;

v o i d ∗pRes ;

i f ( p t h r e a d c r e a t e (&Thr , NULL , s i m p l e T h r e a d , NULL ) < 0 ) { r e t u r n 1 ;

}

p t h r e a d j o i n ( Thr ,& pRes ) ; }

i n t p t h r e a d j o i n ( p t h r e a d t t h r e a d , v o i d ∗∗ r e t v a l ) ;

Za działanie wątku odpowiada funkcja simpleThread, której wskaźnik jest przekazywany do funkcji tworzącej nowy wątek.

Funkcja simpleThread staje się rodzajem funkcji main dla nowego wątku.

Wywołanie funkcji pthread create powoduje utworzenie wątku i uruchomienie w nim funkcji simpleThread.

Wywołanie funkcji pthread join jest konieczny, aby główny wątek procesu zaczekał na zakończenie wątku wykonywanego przez funkcję simpleThread.

Prototyp funkcji pthread join

(18)

Współbieżność Rodzaje współbieżności

Najprostszy wątek w C

#i n c l u d e <s t d i o . h>

#i n c l u d e <p t h r e a d . h>

v o i d ∗ s i m p l e T h r e a d ( v o i d ∗pArg ) {

p r i n t f ( ” . . . Komunikat watku\n ” ) ; }

i n t main ( ) {

p t h r e a d t Thr ;

v o i d ∗pRes ;

i f ( p t h r e a d c r e a t e (&Thr , NULL , s i m p l e T h r e a d , NULL ) < 0 ) { r e t u r n 1 ;

}

p t h r e a d j o i n ( Thr ,& pRes ) ; }

i n t p t h r e a d j o i n ( p t h r e a d t t h r e a d , v o i d ∗∗ r e t v a l ) ;

Za działanie wątku odpowiada funkcja simpleThread, której wskaźnik jest przekazywany do funkcji tworzącej nowy wątek.

Funkcja simpleThread staje się rodzajem funkcji main dla nowego wątku.

wątku i uruchomienie w nim funkcji simpleThread.

Wywołanie funkcji pthread join jest konieczny, aby główny wątek procesu zaczekał na zakończenie wątku wykonywanego przez funkcję simpleThread.

Prototyp funkcji

Jeżeli główny wątek nie zaczeka na zakończenie wątków pobocznych, to prowadzi do niekontrolowanego przerwania ich działania.

(19)

Współbieżność Rodzaje współbieżności

Najprostszy wątek w C++

#i n c l u d e <i o s t r e a m >

#i n c l u d e <t h r e a d>

v o i d s i m p l e T h r e a d ( ) {

s t d : : c o u t << ” . . . Komunikat watku\n ” ; }

i n t main ( ) {

s t d : : t h r e a d Thr ( s i m p l e T h r e a d ) ; Thr . j o i n ( ) ;

}

W najprostszym przypadku tworzenie nowych wątków w C++ jest analogiczne do tego, jakie jest znane z C. Tworzenie wątku można zrealizować w konstruktorze obiektu klasty std::thread, który będzie przechowywał dane o tym wątku.

Jako parametr konstruktora przekazujemy wskaźnik na funkcję wykonującą dany wątek.

Metoda join realizuje tę samą operację jak funkcja pthread create. Nie potrzebuje ona dodatkowych parametrów, gdyż wszystkie niezbędne dane znajdzie w obiekcie Thr.

(20)

Kompilacja i konsolidacja

Dla wersji g++ 5.0.0

g++ -std=c++11 watek.cpp -lpthread

Co najmniej od wersji g++ 6.3.0

g++ watek.cpp -lpthread

Chcą skorzystać z możliwości tworzenia wątków i ich obsługi niezbędne

jest konsolidowanie programu z biblioteką pthread. Zawiera ona

implementację wątków opartą na standardzie POSIX, której API jest

dostosowane dla języków C/C++.

(21)

Współbieżność Rodzaje współbieżności

Jak było w C++

c l a s s B e z K o p i o w a n i a { p u b l i c :

B e z K o p i o w a n i a ( ) {}

} ;

i n t main ( ) {

B e z K o p i o w a n i a B1 , B2 ;

B e z K o p i o w a n i a B3 ( B1 ) ; // Co z r o b i c , a b y z a b r o n i c t e g o

}

(22)

Jak było w C++ – stare rozwiązanie

c l a s s B e z K o p i o w a n i a { p u b l i c :

B e z K o p i o w a n i a ( ) {}

p r i v a t e :

B e z K o p i o w a n i a ( c o n s t B e z K o p i o w a n i a &) {}

} ;

i n t main ( ) {

B e z K o p i o w a n i a B1 , B2 ;

B e z K o p i o w a n i a B3 ( B1 ) ; // T e r a z j u z n i e mozna

}

(23)

Współbieżność Rodzaje współbieżności

Jak jest w C++11 i wyżej

c l a s s B e z K o p i o w a n i a { p u b l i c :

B e z K o p i o w a n i a ( ) {}

B e z K o p i o w a n i a ( c o n s t B e z K o p i o w a n i a &) = d e l e t e ; } ;

i n t main ( ) {

B e z K o p i o w a n i a B1 , B2 ;

B e z K o p i o w a n i a B3 ( B1 ) ; // T e r a z j u z n i e mozna

}

(24)

Parametry i struktury danych wątka

#i n c l u d e <i o s t r e a m >

#i n c l u d e <t h r e a d>

c l a s s B a c k g r o u n d T a s k { p u b l i c :

v o i d o p e r a t o r ( ) ( ) c o n s t {

s t d : : c o u t << ” . . . Komunikat watku\n ” ; }

} ;

i n t main ( ) {

B a c k g r o u n d T a s k o S i m p l e T a s k ; s t d : : t h r e a d Thr ( o S i m p l e T a s k ) ;

Thr . j o i n ( ) ; }

Operator funkcyjny ’() const’ przeciążamy wtedy, gdy nie

(25)

Współbieżność Rodzaje współbieżności

Parametry i struktury danych wątka

#i n c l u d e <i o s t r e a m >

#i n c l u d e <t h r e a d>

c l a s s B a c k g r o u n d T a s k { p u b l i c :

v o i d o p e r a t o r ( ) ( ) {

s t d : : c o u t << ” . . . Komunikat watku\n ” ; }

} ;

i n t main ( ) {

B a c k g r o u n d T a s k o S i m p l e T a s k ; s t d : : t h r e a d Thr ( o S i m p l e T a s k ) ;

Thr . j o i n ( ) ; }

Przeważnie jednak chcemy modyfikować obiekt. Przeciążamy

wówczas operator funkcyjny ().

(26)

Parametry i struktury danych wątka

#i n c l u d e <i o s t r e a m >

#i n c l u d e <t h r e a d>

c l a s s B a c k g r o u n d T a s k { p u b l i c :

v o i d o p e r a t o r ( ) ( ) c o n s t { s t d : : c o u t << ” . . . Komunikat watku ( c o n )\ n ” ; } v o i d o p e r a t o r ( ) ( ) { s t d : : c o u t << ” . . . Komunikat watku ( mod)\ n ” ; } } ;

i n t main ( ) {

B a c k g r o u n d T a s k o S i m p l e T a s k ; s t d : : t h r e a d Thr ( o S i m p l e T a s k ) ;

Thr . j o i n ( ) ; }

Jeśli oba operatory są przeciążone, to wywołany zostanie

operator dla obiektu modyfikowalnego.

(27)

Współbieżność Rodzaje współbieżności

Inicjalizacja wątku bez dodatkowego obiektu

#i n c l u d e <i o s t r e a m >

#i n c l u d e <t h r e a d>

c l a s s B a c k g r o u n d T a s k { p u b l i c :

v o i d o p e r a t o r ( ) ( ) {

s t d : : c o u t << ” . . . Komunikat watku\n ” ; }

} ;

i n t main ( ) {

s t d : : t h r e a d Thr{ B a c k g r o u n d T a s k ( ) } ;

Thr . j o i n ( ) ; }

Do obiektu inicalizującego nowy wątek można przekazać obiekt

reprezentujący wątek poprzez listę w stylu języka C.

(28)

Inicjalizacja wątku bez dodatkowego obiektu

#i n c l u d e <i o s t r e a m >

#i n c l u d e <t h r e a d>

c l a s s B a c k g r o u n d T a s k { p u b l i c :

v o i d o p e r a t o r ( ) ( ) c o n s t {

s t d : : c o u t << ” . . . Komunikat watku\n ” ; }

} ;

i n t main ( ) {

s t d : : t h r e a d Thr ( B a c k g r o u n d T a s k ( ) ) ; // <−− Tak NIE MOZNA!

Thr . j o i n ( ) ; }

Nie można jednak tworzyć obiektu tymczasowego bezpośrednio

w liście parametrów tego konstruktora.

(29)

Współbieżność Rodzaje współbieżności

Inicjalizacja wątku bez dodatkowego obiektu

#i n c l u d e <i o s t r e a m >

#i n c l u d e <t h r e a d>

c l a s s B a c k g r o u n d T a s k { p u b l i c :

v o i d o p e r a t o r ( ) ( ) c o n s t {

s t d : : c o u t << ” . . . Komunikat watku\n ” ; }

} ;

i n t main ( ) {

s t d : : t h r e a d Thr ( ( B a c k g r o u n d T a s k ( ) ) ) ;

Thr . j o i n ( ) ; }

Należy zastosować dodatkowe nawiasy. Tak już jest OK.

(30)

Inicjalizacja wątku bez dodatkowego obiektu

#i n c l u d e <i o s t r e a m >

#i n c l u d e <t h r e a d>

i n t main ( ) {

s t d : : t h r e a d Thr ( [ ] {

s t d : : c o u t << ” . . . Komunikat watku\n ” ; }

) ;

Thr . j o i n ( ) ; }

Funkcję wykonywaną w wątku można zdefiniować jako funkcję

lambda.

(31)

Współbieżność Rodzaje współbieżności

Dostęp do danych

#i n c l u d e <i o s t r e a m >

#i n c l u d e <mutex>

#i n c l u d e <t h r e a d>

s t d : : mutex oMutex ;

c l a s s B a c k g r o u n d T a s k { p u b l i c :

v o i d o p e r a t o r ( ) ( ) {

s t d : : l o c k g u a r d <s t d : : mutex> oGuard ( oMutex ) ; s t d : : c o u t << ” . . . Komunikat watku\n ” ; }

} ;

i n t main ( ) {

s t d : : t h r e a d oThr1{ B a c k g r o u n d T a s k ( ) } , oThr2{ B a c k g r o u n d T a s k ( ) } ;

oThr1 . j o i n ( ) ; oThr2 . j o i n ( ) ; }

Tworzenie obiektu std::lock guard umożliwia automatyczne

zwolnienie po uruchomieniu destruktora.

(32)

Dostęp do danych

#i n c l u d e <i o s t r e a m >

#i n c l u d e <mutex>

#i n c l u d e <t h r e a d>

s t r u c t S h a r e d D a t a { s t d : : mutex M u te x ;

d o u b l e Num ;

} ;

S h a r e d D a t a D a t a 4 T h r e a d s ;

c l a s s B a c k g r o u n d T a s k { p u b l i c :

v o i d o p e r a t o r ( ) ( ) {

s t d : : l o c k g u a r d <s t d : : mutex> oGuard ( D a t a 4 T h r e a d s . M u te x ) ; s t d : : c o u t << ” . . . Komunikat watku\n ” ;

} } ;

i n t main ( ) {

s t d : : t h r e a d oThr1{ B a c k g r o u n d T a s k ( ) } , oThr2{ B a c k g r o u n d T a s k ( ) } ;

Obiektu klasy std::mutex dobrze jest skojarzyć z

konkretnymi danymi.

(33)

Współbieżność

Koniec prezentacji

Dziękuję za uwagę

Cytaty

Powiązane dokumenty

Jeżeli w chwili t 0 zostanie przerwane wstrzykiwanie nośników przez emiter (czyli wyłączony zostanie prąd bazy) ładunek Q N będzie stopniowo zanikać jak pokazano na rysunku

UWAGA: Zmieniając amplitudę sygnału wejściowego i OFFSET w generatorze można zmieniać prąd bazy i uzyskać wartości niesymetryczne.. Prąd bazy można obliczyć na

Jeżeli w chwili t 0 zostanie przerwane wstrzykiwanie nośników przez emiter (czyli wyłączony zostanie prąd bazy) ładunek Q N będzie stopniowo zanikać jak pokazano

Jednak język programowania wysokiego poziomu nie jest jasny dla komputera, który rozumie jedynie ciągi zer i jedynek. Dlatego musimy posłużyć się aplikacją,

[4 ] Mark Mitchell, Jefrey Oldham, Alex Samuel, LINUX Programowanie dla zaawansowanych RM 2002. Salama; UNIX Programowanie systemowe, RM

Polonista w świecie tekstów multimedialnych, czyli o kształtowaniu kompetencji komunikacyjnej.

• W przypadku wykorzystania routingu z użyciem stanu łączy, jeśli każdy router zna pełną topologię, każdy z routerów może skonstruować własne okrojone drzewo częściowe

Sarkoidoza koni znana jest także pod innymi nazwami, takimi jak: idiopatyczna choroba ziarniniakowa koni, uogólniona choroba ziarninia- kowa koni, układowa choroba ziarniniakowa