Die Arbeit mit Threads ist seit C++11 deutlich einfacher geworden.
Folgendes Beispiel zeigt wie std::thread verwendet werden kann:
#include
#include
#include
#include
std::vector calcPrimes(int maxNumber) {
std::vector primes;
for (int c = 2; c <= maxNumber; ++c) {
bool prime = [&c]()->bool{
for (int i = 2; i <= sqrt(c); ++i) {
if (c % i == 0) { return false; }
}
return true;
}();
if (prime) {
primes.push_back(c);
}
}
return primes;
}
int main() {
int maxNumber = 1000000;
std::vector primes;
auto t = std::thread([&primes](int n)->void {
auto calculatedPrimes = calcPrimes(n);
primes.insert(primes.end(), calculatedPrimes.begin(), calculatedPrimes.end());
}, maxNumber);
// Do other stuff
t.join();
std::cout << "Generated " << primes.size() << " primes." << std::endl;
}
Aber dieses Beispiel ist nicht threadsicher.
Um es Threadsicher zu machen müssen wir den Vector primes vor konkurrierendem Zugriff schützen.
Ein Beispiel dazu sähe folgendermaßen aus:
#include
#include
#include
#include
#include
class ThreadsafePrimeCalculator
{
public:
void calcPrimes(int maxNumber) {
if (t.joinable()) t.join();
t = std::thread([this](int maxNumber) -> void { _calcPrimes(maxNumber); }, maxNumber);
}
std::vector getPrimes() {
if (t.joinable()) t.join();
std::lock_guard guard{m};
std::vector copy = primes;
return std::move(copy);
}
~ThreadsafePrimeCalculator() {
if (t.joinable()) t.join();
}
private:
void _calcPrimes(int maxNumber) {
std::lock_guard guard{m};
primes.clear();
for (int c = 2; c <= maxNumber; ++c) {
bool prime = [&c]()->bool{
for (int i = 2; i <= sqrt(c); ++i) {
if (c % i == 0) { return false; }
}
return true;
}();
if (prime) {
primes.push_back(c);
}
}
}
std::thread t;
std::vector primes;
std::mutex m;
};
int main() {
int maxNumber = 1000000;
ThreadsafePrimeCalculator tspc{};
tspc.calcPrimes(maxNumber);
// Do other stuff
std::cout << "Generated " << tspc.getPrimes().size() << " primes." << std::endl;
}
Aber um uns das zu sparen bietet C++ ein anderes Feature: std::future
Futures sind Objekte die in der Zukunft ein Ergebnis zurückliefern. Im Grunde sind es Threads mit Rückgabewert.
Das obere Beispiel lässt sich damit Threadsicher umschreiben ohne Lock Guards und Mutex.
#include
#include
#include
#include
#include
std::vector calcPrimes(int maxNumber) {
std::vector primes;
for (int c = 2; c <= maxNumber; ++c) {
bool prime = [&c]()->bool{
for (int i = 2; i <= sqrt(c); ++i) {
if (c % i == 0) { return false; }
}
return true;
}();
if (prime) {
primes.push_back(c);
}
}
return primes;
}
int main() {
int maxNumber = 1000000;
auto a = std::async(std::launch::async, calcPrimes, maxNumber);
// Do other stuff
std::cout << "Generated " << a.get().size() << " primes." << std::endl;
}