Różnice między wybraną wersją a wersją aktualną.
| Both sides previous revision Poprzednia wersja Nowa wersja | Poprzednia wersja | ||
| sk2:cpp11_threads [2021/11/29 10:09] jkonczak [[ekstra] <functional> w C++] | sk2:cpp11_threads [2024/11/07 23:21] (aktualna) jkonczak [[ekstra] Wątki w C++] | ||
|---|---|---|---|
| Linia 1: | Linia 1: | ||
| ===== [ekstra] Wątki w C++ ===== | ===== [ekstra] Wątki w C++ ===== | ||
| Język C+''''+ (od wersji C+''''+11)  ma w bibliotece standardowej obsługę wątków. \\ | Język C+''''+ (od wersji C+''''+11)  ma w bibliotece standardowej obsługę wątków. \\ | ||
| - | 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()''). | + | <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> | 
| Przykład prostego programu: | Przykład prostego programu: | ||
| + | <html><div style="line-height:105%;margin-top:-1.2em"></div></html> | ||
| <code cpp threadHelloWorld.cpp> | <code cpp threadHelloWorld.cpp> | ||
| - | #include <thread> | ||
| #include <cstdio> | #include <cstdio> | ||
| - | void funkcja(){ | + | #include <thread> | 
| - | puts("Hello world"); | + | void funkcja(unsigned count, const char *argument) { | 
| + | while (count--) | ||
| + | printf("Hello %s\n", argument); | ||
| + | /* kod wykonywany w drugim wątku */ | ||
| } | } | ||
| - | int main(){ | + | int main() { | 
| - | std::thread t(funkcja); | + | std::thread t(funkcja, 3, "world"); | 
| + | /* kod wykonywany w pierwszym wątku */ | ||
| t.join(); | t.join(); | ||
| return 0; | return 0; | ||
| } | } | ||
| </code> | </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: | Podsumowanie tworzenia wątków: | ||
| + | <html><div style="line-height:105%;margin-top:-1.2em"></div></html> | ||
| <code cpp> | <code cpp> | ||
| std::thread t1(funkcja);  // tworzy obiekt 't1', tworzy nowy wątek i uruchamia w nim 'funkcja()'. | std::thread t1(funkcja);  // tworzy obiekt 't1', tworzy nowy wątek i uruchamia w nim 'funkcja()'. | ||
| Linia 24: | Linia 62: | ||
| tworzy nowy wątek i uruchamia w nim 'funkcja()' */ | tworzy nowy wątek i uruchamia w nim 'funkcja()' */ | ||
| // thread t3 = t2; // kod NIEPOPRAWNY – wątków nie można kopiować | // 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 | std::thread t4(funkcja2, 1, 2, "z"); /* tworzy obiekt 't4', tworzy nowy wątek programu | ||
| i uruchamia w nim 'funkcja2(1, 2, "z")'*/ | i uruchamia w nim 'funkcja2(1, 2, "z")'*/ | ||
| Linia 29: | Linia 69: | ||
| Podsumowanie zajmowania się wątkiem: | Podsumowanie zajmowania się wątkiem: | ||
| + | <html><div style="line-height:105%;margin-top:-1.2em"></div></html> | ||
| <code cpp> | <code cpp> | ||
| t1.join(); // czeka na zakończenie wątku | t1.join(); // czeka na zakończenie wątku | ||
| Linia 39: | Linia 80: | ||
| </code> | </code> | ||
| - | <html><small></html> | + | <small> | 
| - | W C++ 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 48: | 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> | |
| - | <html></small></html> | + | |
| Przykład synchronizacji między wątkami (producent-konsument): | 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> | <code cpp condVarHelloWorld.cpp> | ||
| - | #include <thread> | ||
| #include <condition_variable> | #include <condition_variable> | ||
| - | #include <mutex> | ||
| #include <iostream> | #include <iostream> | ||
| - | #include <list> | + | #include <mutex> | 
| + | #include <queue> | ||
| + | #include <thread> | ||
| std::mutex mtx; | std::mutex mtx; | ||
| std::condition_variable cv; | std::condition_variable cv; | ||
| - | std::list<std::string> slowa; | + | std::queue<std::string> wiadomosci; | 
| - | thread_local std::string slowo; | + | thread_local std::string wiadomosc; | 
| - | void consumer(){ | + | void producer() { | 
| - | while(true){ | + | while (true) { | 
| + | std::cin >> wiadomosc; | ||
| { | { | ||
| std::unique_lock<std::mutex> lock(mtx); | std::unique_lock<std::mutex> lock(mtx); | ||
| - | cv.wait(lock, []{return slowa.size()!=0;}); | + | wiadomosci.push(wiadomosc); | 
| - | slowo = slowa.front(); | + | cv.notify_one(); | 
| - | slowa.pop_front(); | + | |
| } | } | ||
| - | std::cout << slowo << std::endl; | ||
| } | } | ||
| } | } | ||
| - | int main(){ | + | void consumer() { | 
| - | std::thread t(consumer); | + | while (true) { | 
| - | while(true) { | + | |
| - | std::cin >> slowo; | + | |
| { | { | ||
| std::unique_lock<std::mutex> lock(mtx); | std::unique_lock<std::mutex> lock(mtx); | ||
| - | slowa.push_back(slowo); | + | cv.wait(lock, [] { return !wiadomosci.empty(); }); | 
| - | cv.notify_one(); | + | 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> | </code> | ||
| + | ++++ | ||
| ===== [ekstra] <functional> w C++ ===== | ===== [ekstra] <functional> w C++ ===== | ||
| - | <html><small><small></html> | + | <small> | 
| 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 130: | Linia 267: | ||
| } | } | ||
| </code> | </code> | ||
| - | <html></small></small></html> | + | </small> |