【问题标题】:Why does this code produce a race condition?为什么这段代码会产生竞争条件?
【发布时间】:2012-08-27 22:12:02
【问题描述】:

这是我第一次尝试使用std::future

我要同时解析三个不同的文件。三个功能分别做到这一点。称为parseSentencesparseTagsparseLinks。它们中的每一个都使用std::async 通过一个非常简单的 lambda 函数在单独的线程中启动:[]() { parser->function(); },其中parser 是一个静态变量,函数是我之前命名的三个函数之一。

int parser::start()
{
    int ret = SUCCESS;
    ASSERT( m_output != nullptr );
    static parser * thisParserInstance = this;

    // parsing files
    std::future<int> parseSentence = std::async(std::launch::async, []() { return thisParserInstance->parseSentences(); } );
    std::future<int> parseLinksResult = std::async(std::launch::async, []() { return thisParserInstance->parseLinks(); } );
    std::future<int> parseTagsResult = std::async(std::launch::async, []() { return thisParserInstance->parseTags(); } );

    // retrieving the results
    ret = parseSentence.get();
    const int linksResult = parseLinksResult.get();
    const int tagsResult = parseTagsResult.get();

    if (ret == SUCCESS)
        ret = linksResult == SUCCESS ? tagsResult : linksResult;

    return ret;
}

现在,当我在 gdb 中运行我的程序时,在 std::future 局部变量之一被破坏时会发生分段错误。那时有 2 个线程正在运行。 线程#1 的调用堆栈是here。 线程#2 的调用栈是here

注意第一个调用栈中指向this的指针为空,导致分段错误。

如果有人有线索,我将不胜感激。

【问题讨论】:

  • 不看bug,这个设计还是有点吓人。 thisParserInstance 存在什么?它的名字不正确:它是第一个进入函数实例的 this,forever。为什么不直接使用this
  • 为什么这么复杂?只需将[this] 放入捕获列表...
  • @GManNickG 我应该解释一下。该函数只在程序执行期间被调用一次。我需要将解析器实例传递给 3 个新线程,但我看不到任何更简洁的方法。
  • 你应该发布一个有这个问题的完整程序,这样我们就不必猜测你在其他地方做了什么。
  • 由于解析器实例被传递给所有三个线程,它立即提出了该实例正在做什么的问题。如果您怀疑数据竞争,解析器实例是最明显的罪魁祸首。

标签: c++ multithreading c++11 race-condition


【解决方案1】:

这里有个大问题:

static parser * thisParserInstance = this;

这是在您第一次调用该函数时初始化的,然后在以后的调用中保持不变。因此,如果您在一个对象上调用该函数,销毁该对象,然后在第二个对象上调用它,您实际上将处理一个指向已失效对象的悬空指针。这肯定会产生未定义的行为。

没有理由使用静态变量; lambda 可以捕获this 并作用于正确的对象。或者更简单地说,按照 cmets 中的建议,使用 async 的可变参数形式将 this 绑定到成员函数:

std::async(std::launch::async, &parser::parseSentences, this);

【讨论】:

  • +1,谢谢。我会改变它,但这不可能是错误,因为该函数只被调用一次,是故意的。
  • 尝试捕获 this-ptr,即使您认为它没有帮助。我想你可能会感到惊讶。
  • 无论如何都不需要使用 lambda,std::async(std::launch::async, &amp;parser::parseSentences, this); 可以正常工作。
【解决方案2】:

对不起各位。

这里是解决方案:std::future exception on gcc experimental implementation of C++0x

链接-lpthread后,错误消失。不过,感谢您的其他评论,它们非常有帮助。

【讨论】:

  • 我怀疑 std::future 没有引入内存屏障。也许这就是 -lpthread 的作用。
  • 我认为这不是问题所在,代码已经链接到 libpthread,正如您可以清楚地看到,其中一个堆栈跟踪显示它正在执行来自 libpthread 的代码:#18 0x00007ffff64eaec6 in start_thread () from /lib64/libpthread.so.0
  • @qdii:看看libstdc++ manual中的要求。
  • 嗯,这是您所做的唯一更改,添加 -lpthread 吗?还是你也捕获了这个指针?
猜你喜欢
  • 1970-01-01
  • 2019-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-12
  • 1970-01-01
  • 2021-08-27
相关资源
最近更新 更多