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:26] jkonczak [[ekstra] Wątki 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()'')((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>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(const char * argument){ | + | #include <thread> |
- | printf("Hello %s\n", argument); | + | 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, "world"); | + | 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 31: | 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 41: | 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 50: | 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 132: | Linia 267: | ||
} | } | ||
</code> | </code> | ||
- | <html></small></small></html> | + | </small> |