Использование boost:: lock_guard для простой блокировки общих данных
Я новичок в библиотеке Boost и пытаюсь реализовать простые потоки производителей и потребителей, которые работают в общей очереди. Мой пример реализации выглядит следующим образом:
#include <iostream>
#include <deque>
#include <boost/thread.hpp>
boost::mutex mutex;
std::deque<std::string> queue;
void producer()
{
while (true) {
boost::lock_guard<boost::mutex> lock(mutex);
std::cout << "producer() pushing string onto queue" << std::endl;
queue.push_back(std::string("test"));
}
}
void consumer()
{
while (true) {
boost::lock_guard<boost::mutex> lock(mutex);
if (!queue.empty()) {
std::cout << "consumer() popped string " << queue.front() << " from queue" << std::endl;
queue.pop_front();
}
}
}
int main()
{
boost::thread producer_thread(producer);
boost::thread consumer_thread(consumer);
sleep(5);
producer_thread.detach();
consumer_thread.detach();
return 0;
}
Этот код работает так, как я ожидаю, но когда main
завершается, я получаю
/usr/include/boost/thread/pthread/mutex.hpp:45:
boost::mutex::~mutex(): Assertion `!pthread_mutex_destroy(&m)' failed.
consumer() popped string test from queue
Aborted
(Я не уверен, что результат из consumer
имеет значение в этой позиции, но я его оставил.)
Я делаю что-то неправильно в моем использовании Boost?
Ответы
Ответ 1
Вы передаете свои потоки (производитель и потребитель) объект mutex
, а затем отсоединяете их. Они должны бежать вечно. Затем вы выходите из своей программы, а объект mutex
больше не действует. Тем не менее, ваши потоки все еще пытаются использовать его, они не знают, что это уже недействительно. Если вы использовали определение NDEBUG, у вас был бы coredump.
Вы пытаетесь написать приложение-демона, и это является причиной отсоединения потоков?
Ответ 2
Немного не по теме, но релевантный imo (... ждет пламени в комментариях).
Потребительская модель здесь очень жадная, циклическая и постоянная проверка данных в очереди. Это будет более эффективно (тратить меньше циклов ЦП), если у вас есть ваши потребительские потоки, которые будут разысканы детерминированно, когда данные будут доступны, используя межпоточную сигнализацию, а не эту петлю блокировки и заглядывания. Подумайте об этом так: в то время как очередь пуста, это по существу жесткий цикл, только сломанный необходимостью получить блокировку. Не идеально?
void consumer()
{
while (true) {
boost::lock_guard<boost::mutex> lock(mutex);
if (!queue.empty()) {
std::cout << "consumer() popped string " << queue.front() << " from queue" << std::endl;
queue.pop_front();
}
}
}
Я понимаю, что вы учитесь, но я бы не советовал использовать это в "реальном" коде. Для изучения библиотеки, хотя, это прекрасно. К вашему счету, это более сложный пример, чем необходимо для понимания того, как использовать lock_guard, поэтому вы нацелитесь высоко!
В конце концов, вы, скорее всего, сгенерируете (или лучше, если сможете, повторно используете) код для очереди, которая сигнализирует работникам, когда они должны выполнять работу, и затем вы будете использовать lock_guard
внутри ваших рабочих потоков для посредничества в доступе к общим данных.
Ответ 3
Когда main
завершается, все глобальные объекты уничтожаются. Однако ваши потоки продолжают работать. Поэтому у вас проблемы, потому что потоки обращаются к удаленному объекту.
Нижняя строка - это то, что вы должны прекратить поток перед выходом. Единственное, что нужно сделать, это заставить основную программу ждать (используя boost::thread::join
), пока потоки не закончатся. Возможно, вы захотите предоставить какой-либо способ сигнализировать о том, что потоки завершатся, чтобы сэкономить слишком долго.
Другая проблема заключается в том, что ваш потребительский поток продолжает работать, даже если нет данных. Возможно, вам стоит подождать boost::condition_variable
, пока не будет указано, что есть новые данные.