我正在研究C++中的并发性,并且我正在尝试实现一个多线程回调注册系统。我产生了下面的代码,它应该接受注册请求,直到一个事件发生。在那之后,它应该按照它们被注册的顺序执行所有注册的回调。注册顺序不必是确定的。代码没有按照预期工作。首先,它很少输出“Pushing callback with id”消息。其次,它有时候会挂起(我认为是由争用条件引起的死锁)。我希望能得到帮助,以弄清楚这里发生了什么。如果您发现我使代码的某些部分过于复杂或误用了某些部分,也请指出这一点。
#include <condition_variable>
#include <functional>
#include <iostream>
#include <mutex>
#include <queue>
#include <thread>
class CallbackRegistrar{
public:
void registerCallbackAndExecute(std::function<void()> callback) {
if (!eventTriggered) {
std::unique_lock<std::mutex> lock(callbackMutex);
auto saved_id = callback_id;
std::cout << "Pushing callback with id " << saved_id << std::endl;
registeredCallbacks.push(std::make_pair(callback_id, callback));
++callback_id;
callbackCond.wait(lock, [this, saved_id]{return releasedCallback.first == saved_id;});
releasedCallback.second();
callbackExecuted = true;
eventCond.notify_one();
}
else {
callback();
}
}
void registerEvent() {
eventTriggered = true;
while (!registeredCallbacks.empty()) {
releasedCallback = registeredCallbacks.front();
callbackCond.notify_all();
std::unique_lock<std::mutex> lock(eventMutex);
eventCond.wait(lock, [this]{return callbackExecuted;});
callbackExecuted = false;
registeredCallbacks.pop();
}
}
private:
std::queue<std::pair<unsigned, std::function<void()>>> registeredCallbacks;
bool eventTriggered{false};
bool callbackExecuted{false};
std::mutex callbackMutex;
std::mutex eventMutex;
std::condition_variable callbackCond;
std::condition_variable eventCond;
unsigned callback_id{1};
std::pair<unsigned, std::function<void()>> releasedCallback;
};
int main()
{
CallbackRegistrar registrar;
std::thread t1(&CallbackRegistrar::registerCallbackAndExecute, std::ref(registrar), []{std::cout << "First!\n";});
std::thread t2(&CallbackRegistrar::registerCallbackAndExecute, std::ref(registrar), []{std::cout << "Second!\n";});
registrar.registerEvent();
t1.join();
t2.join();
return 0;
}
1条答案
按热度按时间hwamh0ep1#
除了评论中的优秀建议之外,我在您的代码中发现的主要问题是您设置的
callbackCond
条件变量wait condition。如果releasedCallback.first
不等于savedId
,会发生什么情况?当我运行你的代码时(使用线程安全队列和
eventTriggered
作为原子),我发现问题出在这个等待函数中,如果你在那个函数中放入一个print语句,你会发现你得到了这样的结果:然后永远等待。
事实上,我发现你的代码中使用的条件变量实际上并不需要,你只需要一个,并且它可以存在于线程安全队列中,你在搜索之后将构建这个队列;)
拥有线程安全队列之后,上面的代码可以简化为:
我不确定这是否完全符合您的要求,但它不会死锁。无论如何,这是一个很好的起点,但您需要自带ThreadSafeQueue的实现。