C++ Weekly - Episode 61 脱水版: Storage Duration with Lambdas

26 阅读1分钟

C++ Weekly - Episode 61 脱水版: Storage Duration with Lambdas

本集主要讲解 Lambda 表达式的存储周期.

给定一个 lambda 表达式, 主程序将返回 2, 该变量将会被共享. lambda 表达式具有状态.

int main()
{
    auto l = [val = 0] () mutable { return val++; };

    l();
    l();
    
    return l(); // 2
}       

类似等同于包含了一个局部 static 变量, 如下 l2 程序. 当然, 假如对该 lambda 进行赋值, 这个变量也还是被共享的. 这样的 lambda 不是线程安全的.

int main()
{
    auto l = [] () {
        static auto val = 0;
        return val++;
    };

    auto l2 = l;
    
    l2();
    l2();

    l();
    l();

    return l();  // 4
}      

比如这样来验证, 通过 std::async 来调用该 lambda, (std::launch::async 参数强制开启单独线程执行 lambda. 如下. 如果调用 f.get(), 阻塞等待 std::async 返回, 这时候期望返回结果应该是 5.

若不等待异步返回结果, (注释 f.get()), 这个时候 lambda 中的静态变量 val 将会陷入竞争状态, 该程序的返回结果变成了 4.

int main()
{
    auto l = [] () {
        static auto val = 0;
        return val++;
    };

    auto l2 = l;

    auto f = std::async(std::launch::async, l);

    // f.get();

    l2();
    l2();

    l();
    l();

    return l();
}   

那么, 我们如何处理解决这类共享的状态变量但又期望线程安全的状态 lambda 函数呢? 通过在函数内部使用 thread_local 线程变量. 这个时候, 无论是否阻塞异步线程调用, 结果都为 4.

int main()
{
    auto l = [] () {
        thread_local auto val = 0;
        return val++;
    };

    auto l2 = l;

    auto f = std::async(std::launch::async, l);

    f.get();

    l2();
    l2();

    l();
    l();

    return l();
}