У меня есть многопоточное приложение C ++, которому нужно много случайных чисел в каждом потоке.
До C ++ 11 я использовал «randomizer», который генерирует некоторые случайности в основном потоке с rand()
функция, и он передает разные случайности для каждой подпотока. Затем все подпотоки присоединяются к основному потоку, снова используется рандомизатор, и вызываются новые подпотоки и так далее для N
раз.
Теперь я хотел бы заменить rand()
с C ++ 11, чтобы избежать рандомизатора и генерировать случайные числа в каждом потоке.
Я хотел бы запустить генератор случайных чисел так, чтобы:
* последовательность семян изменяется от бега к бегу
* последовательность случайных чисел в каждом потоке отличается от потока к потоку (также, если поток вызывается в разных циклах)
Я думал, что посева так:
mt19937 rng;
rng.seed(this_thread::get_id().hash());
что хорошо для меня с тех пор this_thread::get_id().hash()
это «случайное» число, но иногда в разных циклах я мог получить одинаковые идентификаторы.
Как я мог бы запустить случайным образом генератор случайных чисел, чтобы избежать получения одинаковой последовательности случайных чисел в некоторых потоках?
Поскольку идентификаторы потоков отличаются, возможно, просто увеличьте значения с помощью часов:
#include <chrono>
...
typedef std::m19937::result_type seed_type;
typename std::chrono::system_clock seed_clock;
auto init_seed = static_cast<seed_type>
(seed_clock.now().time_since_epoch().count());
init_seed += static_cast<seed_type>(this_thread::get_id());
rng.seed(init_seed);
Соответствующее значение по умолчанию для посева:
std::random_device r;
std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
std::mt19937 eng(seed);
Вы можете просто сделать это в каждом потоке, и это должно работать:
#include <algorithm>
#include <functional>
#include <iostream>
#include <iterator>
#include <mutex>
#include <random>
#include <thread>
int main() {
std::mutex iomutex;
std::vector<std::thread> threads;
for (int i = 0 ; i < 10; ++i) {
threads.emplace_back([&iomutex](int tid) {
std::random_device r;
std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
std::mt19937 eng(seed);
std::uniform_int_distribution<> dist(1, 100);
std::lock_guard<std::mutex> ioguard(iomutex);
std::cout << "Thread " << tid << ": ";
std::generate_n(std::ostream_iterator<int>(std::cout, " "), 10, std::bind(dist, eng));
std::cout << '\n';
}, i);
}
for (auto &&t : threads) {
t.join();
}
}
В качестве альтернативы вы можете вычислить начальные значения в главном потоке и передать каждому рабочему данные для инициализации своего механизма. К сожалению, вы не можете пройти seed_seq
, поэтому в приведенном ниже примере я просто пропускаю инициализированный движок.
int main() {
std::mutex iomutex;
std::vector<std::thread> threads;
std::random_device r;
for (int i = 0 ; i < 10; ++i) {
std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
std::mt19937 thread_eng(seed);
threads.emplace_back([&iomutex](int tid, std::mt19937 init_eng) {
std::mt19937 eng(std::move(init_eng));
std::uniform_int_distribution<> dist(1, 100);
std::lock_guard<std::mutex> ioguard(iomutex);
std::cout << "Thread " << tid << ": ";
std::generate_n(std::ostream_iterator<int>(std::cout, " "), 10, std::bind(dist, eng));
std::cout << '\n';
}, i, thread_eng);
}
for (auto &&t : threads) {
t.join();
}
}