В последнее время я играл с многопоточной архитектурой игрового движка и пулами потоков. Теперь ive реализовал базовый класс Kernel
. Этот класс имеет std::vector<std::thread>
, который представляет threadpool. Теперь, следующая функция выполняется в одном потоке в бассейне:Завершение пользовательского std :: thread pool
while(m_IsRunning)
{
std::unique_lock<std::mutex> kernelstateLocker(m_KernelStateMutex);
m_KernelStateCond.wait(kernelstateLocker);
if(m_KernelState == KernelState::KernelShutdown || m_KernelState == KernelState::KernelTerminate)
{
kernelstateLocker.unlock();
//std::cout << "Worker #" << _workerID << std::endl console log here
break;
}
else if(m_KernelState == KernelState::KernelWorkAvailable)
{
...
}
Как вы можете видеть, поток пробуждается если KernelState
изменения переменных. Это может произойти, когда задача добавляется в очередь или когда ядро отключается. Переменная условия завершения ядра вызывается по основному потоку программы, через m_KernelStateCond.notify_all()
. Однако, как я добавил cout
, как видно из комментария, только один из восьми рабочих потоков будет печатать свое имя и идентификатор, указывая, что остальные никогда не заканчиваются. Кто-нибудь знает, почему это так, и как я могу закончить все потоки в моем пуле? В случае, если это имеет значение, моя платформа TDM-GCC-64 5.1 для Windows 10 64-бит.
Update:
Согласно комментарию запроса и SO правил, вот код, который вызывает переменное условие.
std::unique_lock<std::mutex> shutdownLocker(m_IsRunningMutex);
m_ShutdownCond.wait(shutdownLocker, [this](){ return !m_IsRunning; });
if(!m_IsRunning)
{
shutdownLocker.unlock();
m_KernelStateMutex.lock();
m_KernelState = KernelState::KernelShutdown;
m_KernelStateMutex.unlock();
m_KernelStateCond.notify_all();
}
Я уверен, что эта часть моего кода работает, поскольку по крайней мере один из работников нитей фактически закрывается. И для полноты картины, вот мой полный Kernel
класс:
class Kernel : public Singleton<Kernel>
{
public:
void boot(unsigned int _workerCount);
void run();
void shutdown();
void addTask(std::shared_ptr<Task> _task);
private:
friend class Singleton<Kernel>;
Kernel();
~Kernel();
bool m_IsRunning;
KernelState m_KernelState;
std::vector<std::thread> m_Workers;
std::queue<std::shared_ptr<Task>> m_Tasks;
std::vector<std::shared_ptr<Task>> m_LoopTasks;
std::condition_variable m_KernelStateCond;
std::mutex m_KernelStateMutex;
void workTask(unsigned int _workerID);
};
Пожалуйста, отправьте сообщение [mcve]. Если бы я мог догадаться, это потому, что вы не приобретаете мьютекс, прежде чем уведомлять переменную условия. Чтобы гарантировать правильную последовательность всех связанных потоков, необходимо также получить тот же мьютекс, который используется для ожидания переменной условия, чтобы уведомить ту же переменную условия. Однако, поскольку вы не показывали соответствующий код, не может быть предоставлен авторитетный ответ, пока вы не отредактируете сообщение и не включите [mcve] с акцентом на «минимальную» и «полную» часть. –
P.S. Вам не нужен явный «kernelstateLocker.unlock();» когда уникальный замок выходит из сферы действия и уничтожается, об этом позаботятся автоматически. –
Возможно, было бы проще использовать одну из многих существующих реализаций пула потоков. С boost :: asio это еще проще ([пример] (http://stackoverflow.com/questions/19500404/how-to-create-a-thread-pool-using-boost-in-c)) – rustyx