2010-09-16 95 views
11

我是Boost库的新手,并且试图实现在共享队列上运行的简单生产者和消费者线程。我的示例实现是这样的:使用boost :: lock_guard进行简单的共享数据锁定

#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时做错了什么?

回答

8

你给你的线程(生产者&消费者)的mutex对象,然后分离它们。他们应该永远运行。然后退出程序,并且mutex对象不再有效。尽管如此,你的线程仍然尝试使用它,他们不知道它不再有效。如果你使用NDEBUG定义你会得到一个coredump。

你是否试图编写一个守护进程应用程序,这是脱离线程的原因?

+0

我不想写任何特定类型的应用程序 - 我只是试图熟悉Boost.Thread库。快速调试会话期间调用'detach';如果我删除它们,行为是相同的。我最初怀疑在程序退出之前需要停止线程,因此需要“分离”调用。 – kfb 2010-09-16 12:54:33

+7

不要立即从主线程退出,也不要分离消费者和生产者。在主线程中等待,直到你的消费者和生产者工作。当他们完成时加入他们。然后从main退出。 – 2010-09-16 13:08:43

+0

这是有道理的,并且确实停止断言,谢谢! – kfb 2010-09-16 13:31:27

9

有点偏离主题,但相关的imo(......在评论中等待火焰)。

这里的消费者模型非常贪婪,循环和检查队列上的数据不断。如果你的消费者线程在有数据可用时确定性地唤醒,那么使用线程间信号而不是这个锁定和窥视循环会更有效率(浪费更少的CPU周期)。想想这样:当队列是空的,这实质上是一个紧密的循环,只是由于需要获取锁而破坏了。不理想?

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调解对共享数据的访问。

+0

题外话,但肯定有用的信息,谢谢!我同意这是一个非常贪婪的消费者,但是这源于一个早期的测试,其中没有同步,我试图鼓励该程序锁定:) – kfb 2010-09-16 13:30:49

5

main退出时,所有全局对象都被销毁。然而,你的线程会继续运行。因此,您最终遇到了问题,因为线程正在访问已删除的对象。

底线是您必须在退出之前终止线程。唯一的做法是让主程序等待(通过使用boost::thread::join),直到线程完成运行。您可能想要提供某种方式来指示线程完成运行以节省等待时间太长。

另一个问题是即使没有数据,消费者线程也会继续运行。您可能需要等待boost::condition_variable,直到发出有新数据的信号。