Выбранный вами метод на самом деле не работает и приводит к неопределенному поведению из-за условий гонки.Как вы уже догадались, вам нужна условная переменная.
Вот класс Gate
, демонстрирующий, как использовать условную переменную для реализации шлюза, который ждет, пока некоторое количество потоков достигнет его, прежде чем продолжить:
#include <thread>
#include <mutex>
#include <condition_variable>
#include <iostream>
#include <sstream>
#include <utility>
#include <cassert>
struct Gate {
public:
Gate(unsigned int count = 2) : count_(count) { } // How many threads need to reach the gate before it unlocks
Gate(Gate const &) = delete;
void operator =(Gate const &) = delete;
void wait_for_gate();
private:
int count_;
::std::mutex count_mutex_;
::std::condition_variable count_gate_;
};
void Gate::wait_for_gate()
{
::std::unique_lock<::std::mutex> guard(count_mutex_);
assert(count > 0); // Count being 0 here indicates an irrecoverable programming error.
--count_;
count_gate_.wait(guard, [this](){ return this-> count_ <= 0; });
guard.unlock();
count_gate_.notify_all();
}
void f1()
{
::std::ostringstream msg;
msg << "In f1 with thread " << ::std::this_thread::get_id() << '\n';
::std::cout << msg.str();
}
void f2()
{
::std::ostringstream msg;
msg << "In f2 with thread " << ::std::this_thread::get_id() << '\n';
::std::cout << msg.str();
}
void thread_func(Gate &gate)
{
f1();
gate.wait_for_gate();
f2();
}
int main()
{
Gate gate;
::std::thread t1{thread_func, ::std::ref(gate)};
::std::thread t2{thread_func, ::std::ref(gate)};
t1.join();
t2.join();
}
Надеюсь, структура этого кода будет достаточно похожа на ваш код, чтобы вы могли понять, что здесь происходит.При чтении кода кажется, что вы ищете все потоки для выполнения func1
, а затем func2
.Вы не хотите, чтобы func2
работал, пока какой-либо поток выполняет func1
.
. Это можно рассматривать как ворота, в которых все потоки ожидают прибытия в местоположение 'Finished Func1', прежде чем перейти кзапустите func2.
Я тестировал этот код на собственной локальной версии проводника компилятора.
Основным недостатком защелки в другом ответе является то, что он еще не является стандартным C ++.Мой класс Gate
представляет собой простую реализацию класса защелки, упомянутого в другом ответе, и является стандартным C ++.
Основной способ работы условной переменной состоит в том, что она разблокирует мьютекс, ожидает уведомления,затем блокирует этот мьютекс и проверяет условие.Если условие истинно, оно продолжается без разблокировки мьютекса.Если условие ложно, оно начинается снова.
Итак, после того, как переменная условия говорит, что условие истинно, вы должны сделать все, что вам нужно, затем разблокировать мьютекс и уведомить всех, что высделали это.
Мьютекс защищает переменную общего счета.Всякий раз, когда у вас есть общее значение, вы должны защищать его мьютексом, чтобы ни один поток не мог увидеть это значение в несогласованном состоянии.Условие состоит в том, что потоки могут ожидать, пока это число достигнет 0, что указывает на то, что все потоки уменьшили значение переменной count.