Narzędzia użytkownika

Narzędzia witryny


Pasek boczny

sk2:cpp11_threads

[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())1).

Przykład prostego programu:

threadHelloWorld.cpp
#include <thread>
#include <cstdio>
void funkcja(const char * argument){
    printf("Hello %s\n", argument);
}
int main(){
    std::thread t(funkcja, "world");
    t.join();
    return 0;
}

Podsumowanie tworzenia wątków:

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")'*/

Podsumowanie zajmowania się wątkiem:

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ć.

W C++ ustandaryzowano też zmienne lokalne dla wątku – w deklaracji zmiennej wystarczy dodać słowo kluczowe thread_local, np:

thread_local int tid = i.fetch_add(1);

Ponadto, dodano API do zamków, zmiennych warunkowych, zmiennych atomowych i futures

Przykład synchronizacji między wątkami (producent-konsument):

condVarHelloWorld.cpp
#include <thread>
#include <condition_variable>
#include <mutex>
#include <iostream>
#include <list>
 
std::mutex mtx;
std::condition_variable cv;
 
std::list<std::string> slowa;
thread_local std::string slowo;
 
void consumer(){
    while(true){
        {
            std::unique_lock<std::mutex> lock(mtx);
            cv.wait(lock, []{return slowa.size()!=0;});
            slowo = slowa.front();
            slowa.pop_front();
        }
        std::cout << slowo << std::endl;
    }
}
 
int main(){
    std::thread t(consumer);
    while(true) {
        std::cin >> slowo;
        {
            std::unique_lock<std::mutex> lock(mtx);
            slowa.push_back(slowo);
            cv.notify_one();
        }
    }
}

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

1) 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).
sk2/cpp11_threads.txt · ostatnio zmienione: 2021/11/29 10:26 przez jkonczak