2016-03-10 2 views
-1

В последнее время я играл с многопоточной архитектурой игрового движка и пулами потоков. Теперь 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); 
}; 
+1

Пожалуйста, отправьте сообщение [mcve]. Если бы я мог догадаться, это потому, что вы не приобретаете мьютекс, прежде чем уведомлять переменную условия. Чтобы гарантировать правильную последовательность всех связанных потоков, необходимо также получить тот же мьютекс, который используется для ожидания переменной условия, чтобы уведомить ту же переменную условия. Однако, поскольку вы не показывали соответствующий код, не может быть предоставлен авторитетный ответ, пока вы не отредактируете сообщение и не включите [mcve] с акцентом на «минимальную» и «полную» часть. –

+0

P.S. Вам не нужен явный «kernelstateLocker.unlock();» когда уникальный замок выходит из сферы действия и уничтожается, об этом позаботятся автоматически. –

+0

Возможно, было бы проще использовать одну из многих существующих реализаций пула потоков. С boost :: asio это еще проще ([пример] (http://stackoverflow.com/questions/19500404/how-to-create-a-thread-pool-using-boost-in-c)) – rustyx

ответ

0

я понял проблему вне. Это не проблема с моей реализацией пула потоков как таковой, но это связано с тем, что все еще остались задачи, когда ядро ​​закрывалось. Поэтому некоторые рабочие потоки никогда не закрывались, и, таким образом, ядро ​​застряло. Добавление ограничений в задачи, решаемые этим.

Смежные вопросы