[英]CountDownLatch in C++ using Boost Mutexes and Condition
[英]C++ - Condition Variable without Mutexes?
我想我误解了 CV-Mutex 设计模式,因为我正在创建一个似乎不需要互斥锁的程序,只需要 CV。 如果有人可以帮助解释我在这里做错了什么,我很乐意学习。
我正在解析来自 2 个不同帐户的网站的提要。 Alice
, Bob
。 解析任务很慢,所以我有两个单独的线程,每个线程都专用于处理来自Alice
和Bob
的提要。
然后,我有一个线程从网络接收消息并将工作分配给threadA
或threadB
,具体取决于更新消息的对象。 这样,阅读器/网络线程就不会停止, Alice
的消息是有序的, Bob
的消息也是有序的。
我不在乎Alice
线程是否按时间顺序落后于Bob
线程,只要各个帐户提要按顺序排列即可。
这与线程池非常相似,不同之处在于线程基本上被锁定到大小为 2 的固定大小数组,并且我对每个提要使用相同的线程。
我创建了一个AccountThread
class,它维护一个 JSON 消息queue
,以便在 class 中尽快处理。 这是代码:
#include <queue>
#include <string>
#include <condition_variable>
#include <mutex>
using namespace std;
class AccountThread {
public:
AccountThread(const string& name) : name(name) { }
void add_message(const string& d) {
this->message_queue.push(d);
this->cv.notify_all(); // could also do notify_one but whatever
}
void run_parsing_loop() {
while (true) {
std::unique_lock<std::mutex> mlock(lock_mutex);
cv.wait(mlock, [&] {
return this->is_dead || this->message_queue.size() > 0;
});
if (this->is_dead) { break; }
const auto message = this->message_queue.front();
this->message_queue.pop();
// Do message parsing...
}
}
void kill_thread() {
this->is_dead = true;
}
private:
const string& name;
condition_variable cv;
mutex lock_mutex;
queue<string> message_queue;
// To Kill Thread if Needed
bool is_dead;
};
我可以添加 main.cpp 代码,但它本质上只是一个读取器循环,它根据帐户名称调用thread.add_message(message)
。
为什么我在这里需要lock_mutex
? 我看不出它的目的,因为这个 class 本质上是单线程的。 有没有更好的设计模式呢? 我觉得如果我包含了一个我并不真正需要的变量,比如mutex
,那么我在这个任务中使用了错误的设计模式。
我只是在修改我在网上看到的关于线程池实现的一些文章中的代码,并且很好奇......感谢任何帮助。 谢谢!
首先要做的事情是:没有互斥锁就没有condition_variable::wait
。 wait
的接口需要互斥量。 所以关于
我正在创建一个似乎不需要互斥锁的程序,只需要 CV
请注意,需要互斥锁来保护条件变量本身。 如果在没有互斥锁的情况下如何进行数据竞争的概念没有立即意义,请检查为什么 pthreads 的条件变量函数需要互斥锁。
其次,您提供的代码中有多个痛点。 考虑解决问题的这个版本,我将在下面解释问题:
class AccountThread {
public:
AccountThread(const string& name) : name(name)
{
consumer = std::thread(&AccountThread::run_parsing_loop, this); // 1
}
~AccountThread()
{
kill_thread(); // 2
consumer.join();
}
void add_message(const string& d) {
{
std::lock_guard lok(lock_mutex); // 3
this->message_queue.push(d);
}
this->cv.notify_one();
}
private:
void run_parsing_loop()
{
while (!is_dead) {
std::unique_lock<std::mutex> mlock(lock_mutex);
cv.wait(mlock, [this] { // 4
return is_dead || !message_queue.empty();
});
if (this->is_dead) { break; }
std::string message = this->message_queue.front();
this->message_queue.pop();
string parsingMsg = name + " is processing " + message + "\n";
std::cout << parsingMsg;
}
}
void kill_thread() {
{
std::lock_guard lock(lock_mutex);
this->is_dead = true;
}
cv.notify_one(); // 5
}
private:
string name; // 6
mutable condition_variable cv; // 7
mutable mutex lock_mutex;
std::thread consumer;
queue<string> message_queue;
bool is_dead{false}; // 8
};
从上到下指出的问题(在编号的评论中是):
AccountThread
,那么当 class 提供线程时,更容易正确处理。 这样,只有相关的接口被暴露,你可以更好地控制消费者的生命周期和工作。AccountThread
“死亡”时,工人也应该死亡。 在上面的示例中,我通过终止析构函数中的消费者线程来修复此依赖关系。add_message
在您的代码中导致了数据竞争。 由于您打算在不同的线程中运行解析循环,因此在没有关键部分的情况下简单地推送到队列是错误的。this
捕获它会更干净,例如,您可能不需要对捕获的mlock
的引用。kill_thread
不正确。 您需要通知可能正在等待的消费者线程 state 发生了变化。 要正确执行此操作,您需要使用锁保护在谓词中签入的 state。const string &name
的初始版本可能不是您想要的。 成员 const 引用不会延长临时对象的生命周期,并且构造函数的编写方式可能会使实例带有悬空的 state。 即使您进行典型检查,使用 r 值参考版本重载构造函数,您也将依赖于比您的AccountThread
object 存活时间更长的外部字符串。 更好地使用价值成员。is_alive
成员在未初始化的情况下被使用。总而言之,我认为建议的改变指向了正确的方向。 如果您想更深入地了解您提到的 TBB 组件之类的实现方式,还可以检查类似 Go 的通信渠道的实现。 这样的通道(或缓冲区队列)将简化实现以避免手动使用互斥锁、CV 和活动状态:
class AccountThread {
public:
AccountThread(const string& name) : name(name) {
consumer = std::thread(&AccountThread::run_parsing_loop, this);
}
~AccountThread() {
kill_thread();
consumer.join();
}
void add_message(const string& d) { _data.push(d); }
private:
void run_parsing_loop() {
try {
while (true) {
// This pop waits until there's data or the channel is closed.
auto message = _data.pop();
// TODO: Implement parsing here
}
} catch (...) {
// Single exception thrown per thread lifetime
}
}
void kill_thread() { _data.set(yap::BufferBehavior::Closed); }
private:
string name;
std::thread consumer;
yap::BufferQueue<string> _data;
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.