Różnice między wybraną wersją a wersją aktualną.
| Nowa wersja | Poprzednia wersja | ||
| sk2:cpp11_threads [2017/10/29 21:18] jkonczak utworzono | sk2:cpp11_threads [2024/11/07 23:21] (aktualna) jkonczak [[ekstra] Wątki w C++] | ||
|---|---|---|---|
| Linia 1: | Linia 1: | ||
| - | ===== Wątki w C++11 ===== | + | ===== [ekstra] Wątki w C++ ===== | 
| - | Wraz ze standardem C++11 język C++ doczekał się obsługi wątków w bibliotece standardowej. \\ | + | Język C+''''+ (od wersji C+''''+11) ma w bibliotece standardowej obsługę wątków. \\ | 
| - | API do obsługi wątków jest identyczne z ''boost::thread'', z wyjątkiem obsługi przerywania wątków (''thread::interrupt()''). | + | <small>API do obsługi wątków w C+''''+ zostało przeniesione z ''boost::thread'', z wyjątkiem obsługi przerywania wątków (''thread::interrupt()'')((W C+''''+20 pojawiła się klasa ''std::jthread'' która pozwala zasygnalizować wątkowi że powinien się przerwać, ale wywołanie ''request_stop'' __nie__ przerywa wywołań systemowych (np. ''read''). )).</small> | 
| - | Podsumowanie: | + | Przykład prostego programu: | 
| + | <html><div style="line-height:105%;margin-top:-1.2em"></div></html> | ||
| + | <code cpp threadHelloWorld.cpp> | ||
| + | #include <cstdio> | ||
| + | #include <thread> | ||
| + | void funkcja(unsigned count, const char *argument) { | ||
| + | while (count--) | ||
| + | printf("Hello %s\n", argument); | ||
| + | /* kod wykonywany w drugim wątku */ | ||
| + | } | ||
| + | int main() { | ||
| + | std::thread t(funkcja, 3, "world"); | ||
| + | /* kod wykonywany w pierwszym wątku */ | ||
| + | t.join(); | ||
| + | return 0; | ||
| + | } | ||
| + | </code> | ||
| + | <html><div style="margin-top:-1.2em;margin-bottom:-1.2em"></div></html> | ||
| + | ++++ Odpowiednik kodu w C używający pthreads| | ||
| + | <html><div style="line-height:105%;margin-top:-3em"></div></html> | ||
| + | <code cpp threadHelloWorld.c> | ||
| + | #include <pthread.h> | ||
| + | #include <stdlib.h> | ||
| + | #include <stdio.h> | ||
| + | |||
| + | struct funkcja_argumenty{ | ||
| + | unsigned count; | ||
| + | const char * argument; | ||
| + | }; | ||
| + | |||
| + | void * funkcja(void * rawArgs){ | ||
| + | struct funkcja_argumenty *args = rawArgs; | ||
| + | while(args->count--) | ||
| + | printf("Hello %s\n", args->argument); | ||
| + | free(rawArgs); | ||
| + | /* kod wykonywany w drugim wątku */ | ||
| + | return NULL; | ||
| + | } | ||
| + | int main(void){ | ||
| + | pthread_t tid; | ||
| + | struct funkcja_argumenty *args = malloc(sizeof(struct funkcja_argumenty)); | ||
| + | args->count = 3; | ||
| + | args->argument = "world"; | ||
| + | pthread_create(&tid, NULL, funkcja, args); | ||
| + | /* kod wykonywany w pierwszym wątku */ | ||
| + | pthread_join(tid, NULL); | ||
| + | return 0; | ||
| + | } | ||
| + | </code> | ||
| + | ++++ | ||
| + | |||
| + | Podsumowanie tworzenia wątków: | ||
| + | <html><div style="line-height:105%;margin-top:-1.2em"></div></html> | ||
| + | <code cpp> | ||
| + | std::thread t1(funkcja);  // tworzy obiekt 't1', tworzy nowy wątek i uruchamia w nim 'funkcja()'. | ||
| + | std::thread t2; // tworzy pusty obiekt 't2'. | ||
| + | t2 = std::thread(funkcja); /* tworzy nowy obiekt w miejscu "t2", niszczy poprzedni obiekt, | ||
| + | tworzy nowy wątek i uruchamia w nim 'funkcja()' */ | ||
| + | // thread t3 = t2; // kod NIEPOPRAWNY – wątków nie można kopiować | ||
| + | thread t3 = std::move(t2); // ale wątki można przesuwać | ||
| + | |||
| + | std::thread t4(funkcja2, 1, 2, "z"); /* tworzy obiekt 't4', tworzy nowy wątek programu | ||
| + | i uruchamia w nim 'funkcja2(1, 2, "z")'*/ | ||
| + | </code> | ||
| + | |||
| + | Podsumowanie zajmowania się wątkiem: | ||
| + | <html><div style="line-height:105%;margin-top:-1.2em"></div></html> | ||
| <code cpp> | <code cpp> | ||
| - | #include <thread> // właściwy plik nagłówkowy | + | t1.join(); // czeka na zakończenie wątku | 
| - | std::thread t; // tworzy pusty obiekt wątku. | + | |
| - | t = thread(f); // tworzy nowy obiekt wątku w miejscu "t", niszcząc poprzedni obiekt wątku | + | |
| - | // i uruchamia w nim "f" | + | |
| - | // thread t2 = t; // kod NIEPOPRAWNY – wątków nie można kopiować | + | |
| - | std::thread u(g, 1, 2, "z") // tworzy obiekt wątku (u), tworzy nowy wątek programu i uruchamia | + | |
| - | // "g" z argumentami 1, 2 i "z" | + | |
| - | t.join(); // czeka na zakończenie wątku | + | |
| // thread * w = new thread(h); delete w; // niepoprawne – jeśli zmienna reprezentująca wątek  | // thread * w = new thread(h); delete w; // niepoprawne – jeśli zmienna reprezentująca wątek  | ||
| // ginie (''~thread::thread'') przed zakończeniem wątku, | // ginie (''~thread::thread'') przed zakończeniem wątku, | ||
| - | // to program jest abortowany | + | // to program jest przerywany | 
| // Jeśli główny wątek (main) się zakończy, pozostałe są zabijane (zachowanie inne niż np. w Javie) | // Jeśli główny wątek (main) się zakończy, pozostałe są zabijane (zachowanie inne niż np. w Javie) | ||
| - | u.detach() // rozłącza wątek ze zmienną go reprezentującą. Można zmazać zmienną, wątek przeżyje. | + | t2.detach() // rozłącza wątek ze zmienną go reprezentującą. Można zmazać zmienną, wątek przeżyje. | 
| std::thread(i).detach(); // tworzy nowy wątek bez deklaracji zmiennej która będzie go reprezentować. | std::thread(i).detach(); // tworzy nowy wątek bez deklaracji zmiennej która będzie go reprezentować. | ||
| </code> | </code> | ||
| - | <html><small></html> | + | <small> | 
| - | W C++11 ustandaryzowano też zmienne lokalne dla wątku – w deklaracji zmiennej wystarczy dodać słowo kluczowe ''thread_local'', np: | + | W C+%%%%+ ustandaryzowano też zmienne lokalne dla wątku – w deklaracji zmiennej wystarczy dodać słowo kluczowe ''thread_local'', np: | 
| + | <html><div style="line-height:105%;margin-top:-1.2em"></div></html> | ||
| <code cpp> | <code cpp> | ||
| thread_local int tid = i.fetch_add(1); | thread_local int tid = i.fetch_add(1); | ||
| Linia 30: | Linia 90: | ||
| * http://en.cppreference.com/w/cpp/atomic | * http://en.cppreference.com/w/cpp/atomic | ||
| * http://en.cppreference.com/w/cpp/thread | * http://en.cppreference.com/w/cpp/thread | ||
| + | </small> | ||
| + | |||
| + | Przykład synchronizacji między wątkami (producent-konsument): | ||
| + | <html><div style="line-height:105%;margin-top:-1.2em"></div></html> | ||
| + | <code cpp condVarHelloWorld.cpp> | ||
| + | #include <condition_variable> | ||
| + | #include <iostream> | ||
| + | #include <mutex> | ||
| + | #include <queue> | ||
| + | #include <thread> | ||
| + | |||
| + | std::mutex mtx; | ||
| + | std::condition_variable cv; | ||
| + | |||
| + | std::queue<std::string> wiadomosci; | ||
| + | thread_local std::string wiadomosc; | ||
| + | |||
| + | void producer() { | ||
| + | while (true) { | ||
| + | std::cin >> wiadomosc; | ||
| + | { | ||
| + | std::unique_lock<std::mutex> lock(mtx); | ||
| + | wiadomosci.push(wiadomosc); | ||
| + | cv.notify_one(); | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | void consumer() { | ||
| + | while (true) { | ||
| + | { | ||
| + | std::unique_lock<std::mutex> lock(mtx); | ||
| + | cv.wait(lock, [] { return !wiadomosci.empty(); }); | ||
| + | wiadomosc = wiadomosci.front(); | ||
| + | wiadomosci.pop(); | ||
| + | } | ||
| + | std::cout << wiadomosc << std::endl; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | int main() { | ||
| + | std::thread t(consumer); | ||
| + | producer(); | ||
| + | } | ||
| + | </code> | ||
| + | <html><div style="margin-top:-1.2em;margin-bottom:-1.2em"></div></html> | ||
| + | ++++ Odpowiednik kodu w C używający pthreads| | ||
| + | <html><div style="line-height:105%;margin-top:-3em"></div></html> | ||
| + | <code cpp condVarHelloWorld.c> | ||
| + | #include <pthread.h> | ||
| + | #include <stdio.h> | ||
| + | #include <stdlib.h> | ||
| + | #include <string.h> | ||
| + | |||
| + | struct dumbQueue *msgQueue; | ||
| + | |||
| + | struct dumbQueue* dumbQueue_create (void); | ||
| + | void dumbQueue_push  (struct dumbQueue *q, void *element); | ||
| + | void* dumbQueue_pop  (struct dumbQueue *q); | ||
| + | int  dumbQueue_isEmpty(struct dumbQueue *q); | ||
| + | |||
| + | pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; | ||
| + | pthread_cond_t  cv = PTHREAD_COND_INITIALIZER; | ||
| + | |||
| + | _Thread_local char *msg; | ||
| + | |||
| + | void producer(void) { | ||
| + | while (1) { | ||
| + | msg = malloc(255); | ||
| + | scanf("%*[\n]"); | ||
| + | scanf("%254[^\n]", msg); | ||
| + | { | ||
| + | pthread_mutex_lock(&mtx); | ||
| + | dumbQueue_push(msgQueue, msg); | ||
| + | pthread_cond_signal(&cv); | ||
| + | pthread_mutex_unlock(&mtx); | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | void *consumer(void *_) { | ||
| + | while (1) { | ||
| + | { | ||
| + | pthread_mutex_lock(&mtx); | ||
| + | while (dumbQueue_isEmpty(msgQueue)) | ||
| + | pthread_cond_wait(&cv, &mtx); | ||
| + | msg = dumbQueue_pop(msgQueue); | ||
| + | pthread_mutex_unlock(&mtx); | ||
| + | } | ||
| + | printf("%s\n", msg); | ||
| + | free(msg); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | int main(void) { | ||
| + | msgQueue = dumbQueue_create(); | ||
| + | pthread_t tid; | ||
| + | pthread_create(&tid, NULL, consumer, NULL); | ||
| + | producer(); | ||
| + | } | ||
| + | |||
| + | /*****/  struct dumbQueue { | ||
| + | /* T */ struct dumbQueue_e { | ||
| + | /* h */ void *element; | ||
| + | /* i */ struct dumbQueue_e *next; | ||
| + | /* s */ } *head, *tail; | ||
| + | /* */  }; | ||
| + | /* i */  | ||
| + | /* s */ struct dumbQueue *dumbQueue_create(void) { | ||
| + | /* */  struct dumbQueue *q = malloc(sizeof(struct dumbQueue)); | ||
| + | /* j */ q->head = malloc(sizeof(struct dumbQueue_e)); | ||
| + | /* u */ q->head->next = NULL; | ||
| + | /* s */ q->tail = q->head; | ||
| + | /* t */ return q; | ||
| + | /* */  } | ||
| + | /* a */  | ||
| + | /* */  void dumbQueue_push(struct dumbQueue *q, void *element) { | ||
| + | /* d */ q->tail->next = malloc(sizeof(struct dumbQueue_e)); | ||
| + | /* u */ q->tail = q->tail->next; | ||
| + | /* m */ q->tail->next = NULL; | ||
| + | /* b */ q->tail->element = element; | ||
| + | /* */  } | ||
| + | /* q */  | ||
| + | /* u */ void *dumbQueue_pop(struct dumbQueue *q) { | ||
| + | /* e */ struct dumbQueue_e *next = q->head->next; | ||
| + | /* u */ free(q->head); | ||
| + | /* e */ q->head = next; | ||
| + | /* */  return q->head->element; | ||
| + | /* i */ } | ||
| + | /* m */  | ||
| + | /* p */ int dumbQueue_isEmpty(struct dumbQueue *q) { | ||
| + | /* l */ return q->head->next == NULL; | ||
| + | /*****/  } | ||
| + | </code> | ||
| + | ++++ | ||
| - | <html></small></html> | + | ===== [ekstra] <functional> w C++ ===== | 
| - | ===== <functional> w C++11 ===== | + | <small> | 
| - | <html><small><small></html> | + | |
| Bardzo krótkie streszczenie: | Bardzo krótkie streszczenie: | ||
| + | <html><div style="line-height:105%;margin-top:-1.2em"></div></html> | ||
| <code cpp passingFuncs.cpp> | <code cpp passingFuncs.cpp> | ||
| #include <thread> | #include <thread> | ||
| Linia 58: | Linia 253: | ||
| int main() { | int main() { | ||
| - | thread([]{cout << "A" << endl;}).join();  // lambda – http://en.cppreference.com/w/cpp/language/lambda | + | thread([]{cout << "A" << endl;}).join();  // lambda – http://en.cppreference.com/w/cpp/language/lambda | 
| - | thread(b, 'B').join();  // "zwykła" funkcja | + | thread(b, 'B').join();  // "zwykła" funkcja | 
| - | thread(objC).join();  // operator() | + | thread(objC).join();  // operator() | 
| - | thread(objC, 'D').join();  // operator() z argumentami | + | thread(objC, 'D').join();  // operator() z argumentami | 
| - | thread(bind(&E::f,objE,4)).join();  // funkcja na rzecz obiektu z ustalonym argumentem (currying) | + | thread(bind(&E::f, objE, 4)).join();  // funkcja na rzecz obiektu z ustalonym argumentem (currying) | 
| - | thread(bind(&E::f,objE,_1), 5).join();  // funkcja na rzecz obiektu z nieustalonym argumentem | + | thread(bind(&E::f, objE, _1), 5).join();  // funkcja na rzecz obiektu z nieustalonym argumentem | 
| - | function<char(int)> funR(bind(&E::f,&objE,_1)); // funkcja na rzecz obiektu wołanego przez referencję | + | function<char(int)> funR(bind(&E::f, ref(objE), _1)); // funkcja na rzecz obiektu wołanego przez referencję | 
| - | function<char(int)> funC(bind(&E::f, objE,_1)); // funkcja na rzecz obiektu w stanie z chwili tworzenia funcC | + | function<char(int)> funC(bind(&E::f, objE, _1)); // funkcja na rzecz obiektu w stanie z chwili tworzenia funcC | 
| objE.g();  // modyfikacja obiektu | objE.g();  // modyfikacja obiektu | ||
| thread(funR, 0).join();  // wywołanie zmodyfikowanego obiektu - funcR zna obiekt przez referencję | thread(funR, 0).join();  // wywołanie zmodyfikowanego obiektu - funcR zna obiekt przez referencję | ||
| - | thread(funC, 7).join();  // wywołanie zmodyfikowanego obiektu - funcR zna obiekt przez wartość | + | thread(funC, 7).join();  // wywołanie zmodyfikowanego obiektu - funcC zna obiekt przez wartość | 
| return 0; | return 0; | ||
| } | } | ||
| </code> | </code> | ||
| - | <html></small></small></html> | + | </small> |