使用原子变量的程序意外卡住

问题描述 投票:0回答:1

我有一个程序,利用线程池和原子变量在事件驱动架构中进行同步。然而,它似乎在某些条件下意外地卡住了,我正在努力找出根本原因。

我想实现栅栏事件充当同步点,阻塞事件循环,直到所有前面的事件完成执行。这是通过使用原子变量

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。

尽管有这些机制,程序仍会遇到意外的阻塞问题,可能是由于同步或死锁问题。

有人可以检查我的代码并提供有关它可能意外卡住的原因的见解吗?此外,有什么建议或替代方法来实现类似的功能吗?

c++ asynchronous concurrency stdatomic
1个回答
0
投票

在通知事件循环之前忘记获取

loop_mutex
的锁。

© www.soinside.com 2019 - 2024. All rights reserved.