Podręcznik
2. Współbieżność
2.1. Przekazywanie argumentów i odbieranie wyników
W powyższym przykładzie - parametry do zadania były przekazywane przez wartość - i jest to zalecana metoda. Podobnie w zasadzie był odebrany wynik - przekazaliśmy przez wartość adres w pamięci gdzie należy go umieścić.
W przypadku dużych zbiorów danych przekazywanie przez wartość nie zawsze jednak będzie optymalnym rozwiązaniem. W tym przykladzie z sumowaniem - w klasycznym (nie równoległym) kodzie staralibyśmy się przekazać tablicę do zsumowania przez referencję, by uniknąć jej niepotrzebnego kopiowania. Jeśli jednak zmienimy deklarację funkcji do postaci:
void sortFunction(std::deque<double>& array, double *result)
kod przestanie się kompilować. W przypadku nie stałych referencji C++ wymaga jawnego wywołania std::ref na liście parametrów konstruktora std::thread.
W przypadku odbierania wyniku jest podobnie - jeśli typ argumentu result miałby być referecją do double a nie wskaźnikiem - to powinniśmy przekazać go poprzez std::ref()
Czasem także wynik jest odbierany bezpośrednio w argumencie przekazującym dane wejściowe - jeśli np. naszym zadaniem jest sortowanie wektora. Popatrzmy więc jak nasz przykład mógłby wyglądać jeśli zmienimy sumowanie na sortowanie:
#include "iostream"
#include "sstream"
#include "deque"
#include "random"
#include "algorithm"
#include "thread"
/** Metoda wypełnia tablicę przekazaną jej jako parametr liczbami losowymi.
* Tablica jest wypełniana liczbami losowymi z zakresu 1..100 */
void fillRandmCollection(std::deque<std::deque<double>>& array) {
std::random_device random_device;
std::mt19937 random_engine(random_device());
std::uniform_int_distribution<int> distribution_1_100(1, 100);
for (auto& row : array) {
for (auto& elem : row) {
elem = distribution_1_100(random_engine);
}
}
}
/** Metoda sortuje pojedynczy wiersz */
void sortFunction(std::deque<double>& array) {
std::sort(array.begin(), array.end(), [](double a, double b) {
// zasypiamy na chwilę - by sztucznie wydłużyć jej działanie
std::this_thread::sleep_for(std::chrono::nanoseconds (50));
return a > b;
});
}
int main() {
// tablica do posortowania
std::deque<std::deque<double>> toBeSorted(10, std::deque<double>(1000, 0) );
// losowanie tablicy
fillRandmCollection(toBeSorted);
// sortowanie w jednym wątku
std::cout << "Sortuję w jednym wątku ... " << std::flush;
auto tstart = std::chrono::high_resolution_clock::now();
for (int i=0; i<toBeSorted.size(); ++i) {
sortFunction(toBeSorted[i]);
}
auto tstop = std::chrono::high_resolution_clock::now();
std::cout << " koniec, trwało " << std::chrono::duration_cast<std::chrono::milliseconds>(tstop-tstart).count()/1000.0 << " s\n";
// sortowanie w wielu wątkach
fillRandmCollection(toBeSorted);
std::deque<std::thread> threads;
std::cout << "Sortuję w wielu wątkach ... " << std::flush;
tstart = std::chrono::high_resolution_clock::now();
for (int i=0; i<toBeSorted.size(); ++i) {
threads.emplace_back(sortFunction, std::ref(toBeSorted[i]));
}
for (auto& thread : threads) {
thread.join();
}
tstop = std::chrono::high_resolution_clock::now();
std::cout << " koniec, trwało " << std::chrono::duration_cast<std::chrono::milliseconds>(tstop-tstart).count()/1000.0 << " s\n";
return 0;
}