知道使用 std::once_flag 而不调用 std::call_once (任务成功完成后设置once_flag)

Lou*_* Go 2 c++ multithreading initialization c++11

编辑:这是一个 XY 问题,但也保留原始标题。它可能会帮助其他有相同 XY 问题的人。目标应该是:“有std::once_flag任务成功完成后才翻转”。

使用案例

  1. 初始化只需完成一次,并且可以同时调用。
  2. 在调用初始化之前,需要进行验证。
  3. 如果已经初始化,则不需要验证。

问题

我如何不使用g_init(在下面的示例中)同时避免filesystem::exists()

限制

  1. 我无法修改调用者,这是我无法更改的基础设施。
  2. cfg["PATH"]未知,只能由调用者提供。

欢迎C++20或C++23等解决方案。最好是标准或自制解决方案。也可能是XY问题......

简化的演示

#include <mutex>
#include <filesystem>
#include <atomic>
#include "fmt/core.h"
#include "nlohmann/json.hpp"
using json = nlohmann::json;
std::once_flag g_flag;
std::atomic<bool> g_init{false};

void doOnce(std::string path){
    // initializing from file...
    fmt::print("initialization done");
}

void doWork(json& cfg){
    // Atomic flag for 
    if(g_init) {
        fmt::print("already inited");
        return;
    }

    std::string path = cfg["PATH"];
    if(!std::filesystem::exists(path)){
        fmt::print("load failed");
        return;
    }
    std::call_once(g_flag, doOnce, path);
}

int main() {
    json cfg;
    cfg["PATH"] = "/opt/usr/foo";

    // In the real world case, caller will call it
    // with random values and PATH might not exist.
    doWork(cfg);
}
Run Code Online (Sandbox Code Playgroud)

似乎std::once_flag没有暴露任何东西来知道它是否被调用。

Ala*_*les 5

call_once这正是应该解决的问题。如果你让它完成它的工作,它会为你处理所有这一切。如果初始化失败时抛出异常call_once将再次运行:

void doOnce(std::string path){
    if(!std::filesystem::exists(path)){
        throw std::runtime_error("load failed");
    }
    // initializing from file...
    fmt::print("initialization done");
}

void doWork(json& cfg){
    std::string path = cfg["PATH"];
    try
    {
        std::call_once(g_flag, doOnce, path);
    }
    catch (std::exception& ex)
    {
        fmt::print(ex.what());
    }
}
Run Code Online (Sandbox Code Playgroud)