代码之家  ›  专栏  ›  技术社区  ›  sam

了解循环中std::async中引用的作用域和生存期

  •  0
  • sam  · 技术社区  · 3 年前

    我的问题集中在listDirs函数中的for循环上,我在那里启动异步任务。我通过参考路径 std::async 然后在单独的线程中调用listDir函数。

    我知道,一旦for循环移动到下一次迭代,path变量,它是对 std::filesystem::path 路径向量中的实例超出范围。但是,listDir函数的参数是一个引用,应该绑定到路径。

    我的理解是,即使路径超出了listDirs函数的范围 std::filesystem::path 路径向量中的实例在listDirs函数的整个持续时间内都会持续存在,正如我们所经过的那样 std::ref 。但我不确定这种理解是否正确。

    有人能澄清一下这是怎么回事吗?具体而言:

    是否在中执行std::ref std::async 即使路径超出listDirs函数的范围,也要确保listDir获得有效的引用? 在这种情况下,是否存在悬空引用的风险?

    #include <filesystem>
    using Iterator = std::filesystem::directory_iterator;
    // The caller of this function is the thread runtime
    std::vector<std::string> listDir(const std::filesystem::path& directory)
    {
        
        
        std::vector<std::string> files;
        for (Iterator it(directory); it != Iterator(); ++it)
        {
            
            if (it->is_regular_file())
            {
                files.emplace_back(it->path().filename().string());
                
            }
            
        }
        // When we return this vector as the final action in the function, Return Value Optimization(RVO) takes place to
        // eliminate any extra copying of the vector
        return files;
    
    }
    
    std::vector<std::string> listDirs(const std::vector<std::filesystem::path>& paths)
    {
        using Iterator = std::filesystem::directory_iterator;
        std::vector<std::future<std::vector<std::string>>> futures; // listDir returns std::vector<std::string> type
        // iterate over all the directory paths
        for (const std::filesystem::path& path : paths)
        {
        // start each thread using std::async
            futures.emplace_back(std::async(listDir, std::ref(path)));
        }
        std::vector<std::string> allFiles;
        for (std::future<std::vector<std::string>>& fut : futures)
        {
    
            std::vector<std::string> files = fut.get(); // RVO
            std::move(files.begin(), files.end(), std::back_inserter(allFiles));
    
        }
        // When we return this vector as the final action in the function, Return Value Optimization(RVO) takes place to
        // eliminate any extra copying of the vector
        return allFiles;
    }
    int main()
    {
        std::filesystem::path currentPath("G:\\lesson4");
        std::vector<std::filesystem::path> paths;
    
        for (Iterator it(currentPath); it!= Iterator(); ++it)
        {
            if (it->is_directory())
            {
                std::cout << it->path() << '\n';
                paths.emplace_back(it->path());
            }
            
        }
    
        for (const auto& fileName : listDirs(paths))
        {
            std::cout << fileName << std::endl;
        }
    
    }
    
    1 回复  |  直到 3 年前
        1
  •  1
  •   paddy    3 年前

    在循环中,变量 path 是 reference 。你可以把它想象成一个指针,但它不是。

    for (const std::filesystem::path& path : paths)
    {
        // start each thread using std::async
        futures.emplace_back(std::async(listDir, std::ref(path)));
    }
    

    在循环的第一次迭代时, 路径 指矢量的第一个元素 paths 在第二次迭代时,它指的是向量的第二个元素。等等

    因为 路径 在对其元素的任何引用(即使是在 futures ),这是安全的。当你通过 路径 进入 std::async 构造函数 std::ref(path) ,该引用包装器将封装 现在的 参考

    事实上 reference wrappers 是 通常使用隐藏的指针来实现,因为这是将引用作为 左值 。

    即使循环在调用第一个异步方法之前移动到第二次迭代,引用绑定也保持不变,并且 还 指的是 路径 。