下面是一个使用线程安全无界队列的非常简单的生产者/消费者问题示例。任何人都可以解释为什么这个代码在使用GNU C ++编译时行为正确,但是当使用LLVM C ++编译时,Consumer线程会随机放弃?
#include <iostream>
#include <queue>
#include <math.h>
#include <time.h>
#include "boost/thread/condition_variable.hpp"
#include "boost/thread.hpp"
//
// THREAD SAFE QUEUE
//
template<typename Data>
class Concurrent_Queue
{
private:
std::queue<Data> the_queue;
mutable boost::mutex the_mutex;
boost::condition_variable the_condition_variable;
public:
void push(Data const& data)
{
boost::mutex::scoped_lock lock(the_mutex);
the_queue.push(data);
lock.unlock();
printf("\n...just pushed, waking a thread...\n\n");
the_condition_variable.notify_one();
}
bool empty() const
{
boost::mutex::scoped_lock lock(the_mutex);
return the_queue.empty();
}
bool try_pop(Data& popped_value)
{
boost::mutex::scoped_lock lock(the_mutex);
if(the_queue.empty())
{
return false;
}
popped_value=the_queue.front();
the_queue.pop();
return true;
}
void wait_and_pop(Data& popped_value)
{
boost::mutex::scoped_lock lock(the_mutex);
while(the_queue.empty())
{
printf("\n...buffer empty, waiting to pop...\n\n");
the_condition_variable.wait(lock);
}
popped_value=the_queue.front();
the_queue.pop();
}
int len() {
boost::mutex::scoped_lock lock(the_mutex);
return (int)the_queue.size();
}
};
//
// PRODUCER
//
class Producer {
private:
Concurrent_Queue<int> *buff;
int next;
public:
Producer(Concurrent_Queue<int> *q): buff(q) { printf("Prod up!\n"); }
~Producer() {}
void run() {
int wait_time = 0;
while(1) {
wait_time = (rand()%5)+1;
sleep(wait_time);
printf("wait_time: %d\n", wait_time);
buff->push(wait_time);
printf("buffer_len: %d\n", buff->len());
}
}
};
//
// CONSUMER
//
class Consumer {
private:
Concurrent_Queue<int> * buff;
public:
Consumer(Concurrent_Queue<int> *q): buff(q) { printf("Con up!\n"); }
~Consumer() {}
void run() {
unsigned wait_time = 0;
int latest = 0;
while(1) {
wait_time = (rand()%7)+1;
sleep(wait_time);
buff->wait_and_pop(latest);
printf("latest consumed int: %d\n", latest);
printf("cons buff_len: %d\n", buff->len());
}
}
};
//
// MAIN
//
int main(int argc, const char * argv[])
{
srand((unsigned)time(NULL));
Concurrent_Queue<int> Con_Q;
Consumer taker(&Con_Q);
// sleep(3);
Producer giver(&Con_Q);
boost::thread* prod_thread = new boost::thread(boost::bind(&Producer::run, &giver));
boost::thread* cons_thread = new boost::thread(boost::bind(&Consumer::run, &taker));
prod_thread->join();
cons_thread->join();
}
答案 0 :(得分:2)
您应该在互斥锁下移动通知调用。
这是在pthreads(7)联机帮助页中的某处记录的。我试着找到它。
更新我目前能够找到的最相关的报价是:
线程可以调用
pthread_cond_broadcast()
或pthread_cond_signal()
函数,无论它是否当前拥有调用pthread_cond_wait()
或pthread_cond_timedwait()
的线程与条件变量关联的互斥锁在等待期间;但是,如果需要可预测的调度行为,则该互斥锁应由调用pthread_cond_broadcast()
或pthread_cond_signal()
的线程锁定。如果cond上当前没有线程被阻止,
pthread_cond_broadcast()
和pthread_cond_signal()
函数将无效。
我知道像Helgrind这样的线程检查工具会抱怨如果在锁定之外发出了一个条件。
附注:
我碰巧在前几天编写了一个带有任务队列的thread_pool,它也支持shutdown。您可以尝试这是否会出现Mac上的症状:
bool empty() const
并不是真的有用,因为它是一个全新的电话。如果它将锁转移到调用者
int len() const
有同样的问题您可以使用cv::wait()
的谓词版来获得更清晰的代码:
void wait_and_pop(Data& popped_value)
{
namespace phx = boost::phoenix;
boost::unique_lock<boost::mutex> lock(the_mutex);
//if (the_queue.empty()) printf("\n...buffer empty, waiting to pop...\n\n");
the_condition_variable.wait(lock, !phx::bind(&queue_t::empty, phx::ref(the_queue)));
popped_value = the_queue.front();
the_queue.pop();
}
我更喜欢使用类似于c ++ 11的界面(unique_lock<>
而不是mutex::scoped_lock
),因此更容易切换。
next
- 我删除了它这是我的版本经过微小修改,因此您可以复制/粘贴以检查MacOS(我没有Mac):
#include <iostream>
#include <queue>
#include "boost/thread.hpp"
#include "boost/phoenix.hpp"
//
// THREAD SAFE QUEUE
//
template<typename Data>
class Concurrent_Queue
{
private:
typedef std::queue<Data> queue_t;
queue_t the_queue;
mutable boost::mutex the_mutex;
boost::condition_variable the_condition_variable;
public:
void push(Data const& data)
{
boost::lock_guard<boost::mutex> lock(the_mutex);
the_queue.push(data);
printf("\n...just pushed, waking a thread...\n\n");
the_condition_variable.notify_one();
}
#ifdef UNUSED_CODE
bool empty() const
{
boost::lock_guard<boost::mutex> lock(the_mutex);
return the_queue.empty();
}
bool try_pop(Data& popped_value)
{
boost::lock_guard<boost::mutex> lock(the_mutex);
if(the_queue.empty())
{
return false;
}
popped_value=the_queue.front();
the_queue.pop();
return true;
}
#endif
void wait_and_pop(Data& popped_value)
{
namespace phx = boost::phoenix;
boost::unique_lock<boost::mutex> lock(the_mutex);
//if (the_queue.empty()) printf("\n...buffer empty, waiting to pop...\n\n");
the_condition_variable.wait(lock, !phx::bind(&queue_t::empty, phx::ref(the_queue)));
popped_value = the_queue.front();
the_queue.pop();
}
std::size_t len() {
boost::lock_guard<boost::mutex> lock(the_mutex);
return the_queue.size();
}
};
//
// PRODUCER
//
class Producer {
private:
Concurrent_Queue<int> &buff;
public:
Producer(Concurrent_Queue<int> &q): buff(q) { printf("Prod up!\n"); }
~Producer() {}
void run() {
int wait_time = 0;
while(1) {
wait_time = (rand()%5)+1;
boost::this_thread::sleep_for(boost::chrono::seconds(wait_time));
printf("wait_time: %d\n", wait_time);
buff.push(wait_time);
printf("buffer_len: %lu\n", buff.len());
}
}
};
//
// CONSUMER
//
class Consumer {
private:
Concurrent_Queue<int> & buff;
public:
Consumer(Concurrent_Queue<int> &q): buff(q) { printf("Con up!\n"); }
~Consumer() {}
void run() {
unsigned wait_time = 0;
int latest = 0;
while(1) {
wait_time = (rand()%7)+1;
boost::this_thread::sleep_for(boost::chrono::seconds(wait_time));
buff.wait_and_pop(latest);
printf("latest consumed int: %d\n", latest);
printf("cons buff_len: %lu\n", buff.len());
}
}
};
//
// MAIN
//
int main()
{
srand((unsigned)time(NULL));
Concurrent_Queue<int> Con_Q;
Consumer taker(Con_Q);
//boost::this_thread::sleep_for(boost::chrono::seconds(3));
Producer giver(Con_Q);
boost::thread_group group;
group.create_thread(boost::bind(&Producer::run, &giver));
group.create_thread(boost::bind(&Consumer::run, &taker));
group.join_all();
}