Я использую SynchronisedQueue
общаться между потоками. Я обнаружил, что уничтожение объекта потока, когда присоединяющий поток ожидает переменную условия, приведет к сбою программы. Это можно исправить, позвонив detach()
до разрушения нити. Но мне интересно, что именно происходит, когда поток, ожидающий условную переменную, завершается. Есть ли другой способ использовать условную переменную, чтобы избежать этого?
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
template <typename Type> class SynchronisedQueue {
public:
void Enqueue(Type const & data) {
std::unique_lock<std::mutex> lock(mutex_);
queue_.push(data);
condition_.notify_one();
}
Type Dequeue() {
std::unique_lock<std::mutex> lock(mutex_);
while (queue_.empty())
condition_.wait(lock);
Type result = queue_.front();
queue_.pop();
return result;
}
private:
std::queue<Type> queue_;
std::mutex mutex_;
std::condition_variable condition_;
};
class Worker {
public:
Worker(SynchronisedQueue<int> * queue) : queue_(queue) {}
void operator()() {
queue_->Dequeue(); // <-- The thread waits here.
}
private:
SynchronisedQueue<int> * queue_;
};
int main() {
auto queue = new SynchronisedQueue<int>();
Worker worker(queue);
std::thread worker_thread(worker);
worker_thread.~thread(); // <-- Crashes the program.
return 0;
}
Из спецификации C ++ 11:
30.3.1.3 деструктор потока [thread.thread.destr] ~ thread ();
Если joinable (), вызывает std :: terminate (). В противном случае не имеет никакого эффекта.
[Примечание: либо неявное отсоединение, либо присоединение к потоку joinable () в его деструкторе может привести к трудностям в отладке ошибок правильности (для отсоединения) или производительности (для объединения), возникающих только при возникновении исключения). Таким образом, программист должен гарантировать, что деструктор никогда не будет выполнен, пока поток все еще является присоединяемым. — конец примечания]
Так зовет thread destructor
без первого звонка join
(дождаться его окончания) или detach
гарантированно немедленно позвонить std::terminate
и закончить программу.
Деструктор для std::thread
позвоню std::terminate
если он запущен в потоке, если вы не вызвали join()
(дождаться окончания потока) или detach()
(чтобы отделить нить от объекта) на нем.
Ваш код вызывает деструктор для worker_thread
без звонка join()
или же detach()
на нем и так std::terminate
называется. Это не связано с наличием условных переменных.
Вы никогда не сможете уничтожить ресурс, пока что-то его использует или может использовать. Это действительно просто здравый смысл.