了解循环内 std::async 中引用的范围和生命周期

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

我的问题集中在 listDirs 函数中的 for 循环,我在其中启动异步任务。我通过引用

std::async
传递路径,然后在单独的线程中调用 listDir 函数。

我知道,一旦 for 循环移动到下一次迭代,路径变量(对路径向量中的

std::filesystem::path
实例的 const 引用)就会超出范围。但是,listDir 函数的参数是一个引用,应该绑定到路径。

我的理解是,即使路径超出了 listDirs 函数的范围,路径向量中的实际

std::filesystem::path
实例在 listDirs 函数的整个持续时间内仍然存在,因为我们经过
std::ref
。但我不确定这个理解是否正确。

有人可以澄清这是如何工作的吗?具体来说:

std::async
中的std::ref是否确保即使path超出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;
    }

}
c++ multithreading asynchronous
1个回答
1
投票

在循环中,变量

path
是一个 引用。你可以把它想象成一个指针,但它不是。

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

在循环的第一次迭代中,

path
指的是向量
paths
的第一个元素。在第二次迭代时,它指的是向量的第二个元素。等等...

因为

paths
在其元素的任何引用的生命周期内都不会改变(即使是在
futures
中使用的元素),所以这是安全的。当您使用
path
std::async
传递到
std::ref(path)
构造函数时,该引用包装器将封装 current 引用。

事实上,引用包装器通常使用底层的指针来实现,因为这是将引用作为左值传递的唯一实用方法。

即使在调用第一个异步方法之前循环移至第二次迭代,引用绑定也保持不变,并且 still 引用

paths
的第一个元素。

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