Я пытаюсь реализовать некоторый алгоритм, используя потоки, которые должны быть синхронизированы в какой-то момент.Более или менее последовательность для каждого потока должна быть:
1. Try to find a solution with current settings.
2. Synchronize solution with other threads.
3. If any of the threads found solution end work.
4. (empty - to be inline with example below)
5. Modify parameters for algorithm and jump to 1.
Вот игрушечный пример с алгоритмом, измененным на генерацию случайных чисел - все потоки должны завершиться, если хотя бы один из них найдет 0.
#include <iostream>
#include <condition_variable>
#include <thread>
#include <vector>
const int numOfThreads = 8;
std::condition_variable cv1, cv2;
std::mutex m1, m2;
int lockCnt1 = 0;
int lockCnt2 = 0;
int solutionCnt = 0;
void workerThread()
{
while(true) {
// 1. do some important work
int r = rand() % 1000;
// 2. synchronize and get results from all threads
{
std::unique_lock<std::mutex> l1(m1);
++lockCnt1;
if (r == 0) ++solutionCnt; // gather solutions
if (lockCnt1 == numOfThreads) {
// last thread ends here
lockCnt2 = 0;
cv1.notify_all();
}
else {
cv1.wait(l1, [&] { return lockCnt1 == numOfThreads; });
}
}
// 3. if solution found then quit all threads
if (solutionCnt > 0) return;
// 4. if not, then set lockCnt1 to 0 to have section 2. working again
{
std::unique_lock<std::mutex> l2(m2);
++lockCnt2;
if (lockCnt2 == numOfThreads) {
// last thread ends here
lockCnt1 = 0;
cv2.notify_all();
}
else {
cv2.wait(l2, [&] { return lockCnt2 == numOfThreads; });
}
}
// 5. Setup new algorithm parameters and repeat.
}
}
int main()
{
srand(time(NULL));
std::vector<std::thread> v;
for (int i = 0; i < numOfThreads ; ++i) v.emplace_back(std::thread(workerThread));
for (int i = 0; i < numOfThreads ; ++i) v[i].join();
return 0;
}
У меня есть вопросы о разделах 2. и 4. из кода выше.
A) В разделе 2 происходит синхронизация всех потоков и сбор решений (если они найдены).Все делается с помощью переменной lockCnt1
.По сравнению с однократным использованием condition_variable, мне трудно было установить lockCnt1
на ноль, чтобы иметь возможность повторно использовать этот раздел (2.) в следующий раз.Поэтому я представил раздел 4. Есть ли лучший способ сделать это (без введения раздела 4)?
B) Кажется, что все примеры показывают использование condition_variable, а не в контексте сценария «производитель-потребитель».Есть ли лучший способ синхронизировать все потоки в случае, когда все являются «производителями»?
Редактировать: Просто чтобы прояснить, я не хотел описывать детали алгоритма, так как это не важно здесь - в любом случае это необходимоиметь все решения или ни одного из данного выполнения цикла и смешивать их не допускается.Описанная последовательность выполнения должна соблюдаться, и вопрос заключается в том, как обеспечить такую синхронизацию между потоками.