User Tools

Site Tools


sk2:cpp11_threads

This is an old revision of the document!


[ekstra] Wątki w C++

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). )). Przykład prostego programu: <html>

</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>

</html> ++ Odpowiednik kodu w C używający pthreads

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;
}

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> t1.join(); // czeka na zakończenie wątku // thread * w = new thread(h); delete w; // niepoprawne – jeśli zmienna reprezentująca wątek // ginie (''~thread::thread'') przed zakończeniem wątku, // to program jest przerywany // Jeśli główny wątek (main) się zakończy, pozostałe są zabijane (zachowanie inne niż np. w Javie) 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ć. </code> <small> 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> thread_local int tid = i.fetch_add(1); </code> Ponadto, dodano API do zamków, zmiennych warunkowych, zmiennych atomowych i futures * http://en.cppreference.com/w/cpp/atomic * 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

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;
/*****/  }

++ ===== [ekstra] <functional> w C++ ===== Bardzo krótkie streszczenie:

passingFuncs.cpp
#include <thread>
#include <iostream>
#include <functional>
using namespace std;
using namespace std::placeholders;
 
void b(char c){ cout << c << endl;}
 
class C{
public:
	void operator()() {cout << 'C' << endl;}
	void operator()(char c) {cout << c << endl;}
} objC;
 
class E{
	const char * t_ = "ABCDEFGH";
public: 
	char f(int i){ cout << t_[i] << endl; return t_[i];}
	void g(){t_="G";}
} objE;
 
int main() {
	thread([]{cout << "A" << endl;}).join();              // lambda – http://en.cppreference.com/w/cpp/language/lambda 
	thread(b, 'B').join();                                // "zwykła" funkcja
	thread(objC).join();                                  // operator()
	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, _1), 5).join();              // funkcja na rzecz obiektu z nieustalonym argumentem
	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
	objE.g();                   // modyfikacja obiektu
	thread(funR, 0).join();     // wywołanie zmodyfikowanego obiektu - funcR zna obiekt przez referencję
	thread(funC, 7).join();     // wywołanie zmodyfikowanego obiektu - funcC zna obiekt przez wartość
	return 0;
}

sk2/cpp11_threads.1731018100.txt.gz · Last modified: (external edit)