C++ 从 std::async 函数读取命名空间中的全局变量标志

Posted

技术标签:

【中文标题】C++ 从 std::async 函数读取命名空间中的全局变量标志【英文标题】:C++ read global variable flag in namespace from std::async function 【发布时间】:2018-06-03 11:10:53 【问题描述】:

所以我的用例如下:我在命名空间中定义了一些函数和字段。其中一个函数将初始化字段,然后在对 std::async 的调用中运行另一个函数。被调用的函数应该无限期地运行,直到被标记为停止,最常见的是从它自己的线程之外。基本代码看起来像这样

namespace Game

void Create() 
    Initialize();

    auto returned = std::async(RunGameLoop, std::ref(FLAGS));


现在,我尝试实现的方法是在命名空间中初始化这些标志:

namespace Game

namespace // This is used because the fields and functions here should be for internal use only

bool stopped = false;


在 RunGameLoop 函数内部,基本结构是这样设置的

namespace Game

namespace // This is used because the fields and functions here should be for internal use only

void RunGameLoop()

    while (!stopped)
    
        // ... do stuff
    



但似乎由于异步的工作原理,如果我从 RunGameLoop 函数内部以外的任何地方更改 stopped 的值,则 RunGameLoop 函数看不到任何变化。大概在创建异步函数调用时,C++ 只是在构造时复制范围内的所有值,通过值而不是引用传递它们。

我的问题是:如何在异步函数循环中使这种变化引人注目?甚至更好:有没有更好的方法来用 C++ 中的异步函数来传达像这样的简单全局标志?我已经尝试过使用 std::ref、传递指针和通过引用传递整个标志映射,但似乎在 RunGameLoop 函数之外所做的任何更改都不会在 RunGameLoop 函数内部引起注意。

编辑:我已经设法在一个最小的例子中复制了这个问题,这个程序将无限期地运行,实际上永远不会到达第二个 std::cout 语句,这与直觉相反。实际上,std::async 调用似乎根本没有异步运行该函数,这比我在自己的项目中所经历的要严苛一些。我承认我可能误解了 std::async 应该如何使用,但似乎这段代码应该对我有用。

编辑 2: 我之前的示例搞砸了,所以我修复了它。不幸的是,与我的实际项目不同,现在它似乎表现得如预期:

#include <iostream>
#include <future>

namespace test

namespace


std::atomic<bool> testbool = false;
std::future<void> returned;

void queryTestBool()

    while (!testbool)
    

    
    std::cout << "EXITED THREAD: " << std::boolalpha << testbool << std::endl;



void Initialize()

    testbool = false;


void Delete()

    testbool = !testbool;

    returned.get();


void Create()

    Initialize();

    returned = std::async(queryTestBool);



int main()

    using namespace test;

    std::cout << std::boolalpha << testbool << std::endl;

    Create();

    Delete();

    std::cout << std::boolalpha << testbool << std::endl;

这个程序输出

false

EXITED THREAD: true

true

意味着Delete 函数不仅成功地改变了testbool 的值,而且在异步while 循环中注意到了这种改变。最后一部分是出于某种原因在我自己的项目中没有发生的事情,即使我使用 std::atomic 也是如此。我会进一步调查。

【问题讨论】:

您需要某种形式的同步,例如设置标志atomic&lt;bool&gt;,否则对这个标志的并发读写访问会产生数据竞争,这是一种未定义的行为。 @VTT 我已经尝试过了,虽然出于许多其他原因它很重要,但它实际上并不能解决这个问题 我猜你应该提供一个 mcve……如果在这里使用 atomic 不会使更改可见,那么问题可能是其他问题,例如使用不同的 stopped 变量。 我看看能否在 MCVE 中重新创建它。然而,问题不在于一般线程安全或当一个线程写入一个变量而另一个线程正在读取它时具有未定义的行为,而是关于在 std::async 函数中提供指向全局变量的指针而不是简单地复制它,所以我认为我是否使用 std::atomic 与我的问题无关。 我提供了一个以类似方式失败的示例 【参考方案1】:

所以我觉得自己非常愚蠢。在为此苦苦挣扎了几周,实现了各种各样的东西之后,我终于发现我的测试调用 Delete() 函数的地方被跳过了,因为断言失败,我没想到在运行其余部分之前退出测试... 谜团解开了

【讨论】:

以上是关于C++ 从 std::async 函数读取命名空间中的全局变量标志的主要内容,如果未能解决你的问题,请参考以下文章

c++中的异步编程——future,promise

为啥 std::async 使用同一个线程运行函数

C++ std::async 遇到 system_error?

C++学习笔记:高级编程:文件和流,异常处理,动态内存,命名空间

如何从嵌套命名空间中引用外部 C++ 命名空间?

C++并发学习笔记