gpt4 book ai didi

c++ - 两个线程之间通过公共(public)数据结构进行通信。设计问题

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:46:19 25 4
gpt4 key购买 nike

我目前有两个线程,一个生产者和一个消费者。生产者是一个静态方法,在一个Deque类型的静态容器中插入数据,并通过boost::condition_variable通知消费者deque对象中已经插入了一个对象。然后消费者从 Deque 类型中读取数据并将其从容器中移除。两个线程使用 boost::condition_variable

进行通信

这是正在发生的事情的摘要。这是消费者和生产者的代码

    //Static Method : This is the producer. Different classes add data to the container using this method
void C::Add_Data(obj a)
{
try
{
int a = MyContainer.size();
UpdateTextBoxA("Current Size is " + a);
UpdateTextBoxB("Running");
MyContainer.push_back(a);
condition_consumer.notify_one(); //This condition is static member
UpdateTextBoxB("Stopped");
}
catch (std::exception& e)
{
std::string err = e.what();
}
}//end method


//Consumer Method - Runs in a separate independent thread
void C::Read_Data()
{
while(true)
{
boost::mutex::scoped_lock lock(mutex_c);
while(MyContainer.size()!=0)
{
try
{
obj a = MyContainer.front();
....
....
....
MyContainer.pop_front();
}
catch (std::exception& e)
{
std::string err = e.what();
}
}
condition_consumer.wait(lock);
}

}//end method

现在插入到 Deque 类型对象中的对象速度非常快,大约每秒 500 个对象。运行时我注意到 TextBoxB 始终处于“已停止”状态,而我认为它应该切换在“运行”和“停止”之间。加上非常慢。关于我可能没有考虑和可能做错的事情有什么建议吗?

最佳答案

1) 你应该做 MyContainer.push_back(a);在互斥锁下——否则你会得到数据竞争,这是未定义的行为(+你可能也需要通过互斥锁保护 MyContainer.size();,这取决于它的类型和你使用的 C++ISO/编译器版本) .

2) void C::Read_Data()应该是:

void C::Read_Data()
{
scoped_lock slock(mutex_c);
while(true) // you may also need some exit condition/mechanism
{
condition_consumer.wait(slock,[&]{return !MyContainer.empty();});
// at this line MyContainer.empty()==false and slock is locked
// so you may pop value from deque
}
}

3) 您将并发队列的逻辑与生产/消费的逻辑混合在一起。相反,您可以将并发队列部分隔离为独立实体:

LIVE DEMO

// C++98
template<typename T>
class concurrent_queue
{
queue<T> q;
mutable mutex m;
mutable condition_variable c;
public:
void push(const T &t)
{
(lock_guard<mutex>(m)),
q.push(t),
c.notify_one();
}
void pop(T &result)
{
unique_lock<mutex> u(m);
while(q.empty())
c.wait(u);
result = q.front();
q.pop();
}
};

Thanks for your reply. Could you explain the second parameter in the conditional wait statement [&]{return !MyContainer.empty();}

有第二个版本condition_variable::wait它将谓词作为第二个参数。它基本上等待谓词为假,帮助“忽略”spurious wake-ups .

[&]{return !MyContainer.empty();} - 这是 lambda function .它是 C++11 的新特性——它允许“就地”定义函数。如果您没有 C++11,那么只需制作独立谓词或使用 wait 的单参数版本即可。使用手动 while 循环:

while(MyContainer.empty()) condition_consumer.wait(lock);

One question in your 3rd point you suggested that I should Isolate the entire queue while My adding to the queue method is static and the consumer(queue reader) runs forever in a separate thread. Could you tell me why is that a flaw in my design?

“永远运行”或static 没有问题.你甚至可以制作static concurrent_queue<T> member - 如果您的设计需要。

缺陷是多线程同步与其他工作相结合。但是当你有 concurrent_queue - 所有的同步都被隔离在原语中,生产/消费数据的代码不会被锁和等待污染:

concurrent_queue<int> c;
thread producer([&]
{
for(int i=0;i!=100;++i)
c.push(i);
});
thread consumer([&]
{
int x;
do{
c.pop(x);
std::cout << x << std::endl;
}while(x!=11);
});
producer.join();
consumer.join();

如您所见,push/pop 没有“手动”同步, 代码更简洁。

此外,当您以这种方式解耦组件时 - 您可以单独测试它们。此外,它们的可重用性也越来越强。

关于c++ - 两个线程之间通过公共(public)数据结构进行通信。设计问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15913142/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com