我有一个程序,利用线程池和原子变量在事件驱动架构中进行同步。然而,它似乎在某些条件下意外地卡住了,我正在努力找出根本原因。
我想实现栅栏事件充当同步点,阻塞事件循环,直到所有前面的事件完成执行。这是通过使用原子变量
ntasks
来实现的。
ntasks
变量用于计算正在进行的事件任务的数量。当事件开始执行时它会递增 (begin_event()
),当事件完成时它会递减 (end_event()
)。如果计数达到零,则表示事件循环继续进行。
这是我的代码的简化版本。
#include <cstdint>
#include <atomic>
#include <mutex>
#include <thread>
#include <queue>
#include <cstdio>
#include "ThreadPool.h"
enum event_t {
update, render, fence = 0xffffffff
};
std::mutex queue_mutex;
std::queue<event_t> queue;
std::atomic<long long> ntasks;
ThreadPool thread_pool(4);
std::mutex loop_mutex;
std::condition_variable loop_cv;
void publish_event(event_t t) {
std::unique_lock<std::mutex> lock(queue_mutex);
queue.push(t);
}
bool try_get_event(event_t& x) {
std::unique_lock<std::mutex> lock(queue_mutex);
if (queue.empty())
return false;
x = queue.front();
queue.pop();
return true;
}
void onUpdate() {
printf("%s", "Update!");
publish_event(render);
publish_event(fence); // Wait until render event finished
publish_event(update); // Start the next pass
}
void onRender() {
// Pass
}
// Called before event execution start
void begin_event()
{
ntasks.fetch_add(1, std::memory_order_release); // Increase the count of excution tasks
}
void end_event()
{
if (ntasks.fetch_sub(1, std::memory_order_acq_rel) == 1) // Decrease the count of excution tasks
loop_cv.notify_one(); // If there are no more tasks, we can notify to release the fence
}
int main() {
publish_event(update); // Initial event to start the event loop
// Event Loop
while (true) {
event_t event;
if (try_get_event(event)) {
switch (event) {
case update:
begin_event();
thread_pool.enqueue([] { onUpdate(); end_event(); });
break;
case render:
begin_event();
thread_pool.enqueue([] { onRender(); end_event(); });
break;
case fence:
{
std::unique_lock<std::mutex> lock(loop_mutex);
// Wait until there are no more tasks
loop_cv.wait(lock, [] { return ntasks.load(std::memory_order_acquire) <= 0; });
}
break;
}
}
}
}
最奇怪的是,如果我使用调试器或添加一些输出语句,问题就无法重现。
似乎卡在了
loopCv.wait()
点。这是因为当我用自旋锁替换它时,问题就消失了。
case fence:
while (ntasks.load(std::memory_order_acquire) > 0);
使用的线程池来自:https://github.com/progschj/ThreadPool
我使用的平台是MSVC编译器,Windows系统和Intel i7-10750H CPU。
尽管有这些机制,程序仍会遇到意外的阻塞问题,可能是由于同步或死锁问题。
有人可以检查我的代码并提供有关它可能意外卡住的原因的见解吗?此外,有什么建议或替代方法来实现类似的功能吗?
在通知事件循环之前忘记获取
loop_mutex
的锁。