【问题标题】:What goes wrong when passing a std::sub_match as argument to a std::thread?将 std::sub_match 作为参数传递给 std::thread 时出了什么问题?
【发布时间】:2019-07-10 11:56:24
【问题描述】:

我将std::sub_match 作为参数传递给std::thread(请参阅下面的示例代码)。线程函数需要一个 const 字符串引用。 sub_match 可以转换为字符串。所以一切编译正常。

但有时函数接收到错误的字符串。 当我在将 sub_match 传递给线程之前将其转换为字符串时,它会按预期工作。有什么区别?

我认为这是一个竞争条件,因为当线程执行时,原来的 sub_match 可能不再存在。但我认为线程的参数无论如何都会被复制。 如何找出哪些参数可以安全地传递给线程,哪些不能?

#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <regex>
#include <unistd.h>

class test_t {
  public:
    test_t(void) {}
    ~test_t(void) {}

    void start(void){
     //-------------------------------------------------
     // Do some memory allocation.
     // The error seems to appear faster with that.
     std::vector<std::string> vec;
     for(unsigned int i = 0; i < 1000; ++i) {
        vec.push_back("test_test_test");
     }
     //-------------------------------------------------

     std::string event = "operating";
     std::smatch match;
     std::regex expr("\\(operating\\)",
         std::regex_constants::icase | 
         std::regex_constants::basic);

     if(std::regex_match(event, match, expr)) {
        std::cout << "start thread" << std::endl;
        m_thread = std::thread(&test_t::thread_func, this, match[1]);              //NOK
//        m_thread = std::thread(&test_t::thread_func, this, match[1].str());        // OK
//        m_thread = std::thread(&test_t::thread_func, this, (std::string)match[1]); // OK
        m_thread.detach();
        std::cout << "thread started" << std::endl;
     }
    }

  private:
    std::thread m_thread;

    void thread_func(const std::string& string) {
     if(string != "operating") {
        std::cout << "ERROR: string: \"" << string << "\"" << std::endl;
        exit(EXIT_FAILURE);
     } else {
        std::cout << "string: \"" << string << "\"" << std::endl;
     }
    }
};

int main(int argc, char** argv) {
  test_t test;
  while(1) {
    test.start();
    usleep(100);
  }
  return 0;
}

编译消息:

Compiled with: g++ --std=c++11 -pthread -o test main.cpp
g++ --version: g++ (SUSE Linux) 4.8.5

预期输出:

start thread
thread started
string: "operating"
(repeat)

实际输出:

start thread
thread started
string: "operating"
ERROR: string: "test_test"

【问题讨论】:

  • match[1] 返回一个常量引用,对吧?所以,你应该这样传递它,使用std::cref(match[1]),因为std::thread 移动它的所有参数。

标签: c++ multithreading c++11


【解决方案1】:

operator[] for std::smatch 返回 sub_match,它可以被视为匹配字符的迭代器对。

调用regex_match 后,只要event 存在,您就可以使用operator[] 访问子匹配项。当event 被删除时(你没有加入你的线程,所以start 立即返回并且event 被销毁),子匹配有悬空指针并且不应该被访问。


m_thread = std::thread(&test_t::thread_func, this, match[1]);

这不起作用,因为当函数超出范围时,事件被删除并且子匹配有悬空指针。


m_thread = std::thread(&test_t::thread_func, this, match[1].str());

这是因为 str() 返回匹配字符串的副本。


m_thread = std::thread(&test_t::thread_func, this, (std::string)match[1]);

这也有效,因为临时字符串是基于子匹配 match[1] 创建的,并且 temp 被传递到线程中。

【讨论】:

  • thread_func的参数是const std::string&amp;,所以不会创建一个隐式的std::string临时吗?
  • sub_match 有 operator string(),但在这一行中 &amp;test_t::thread_func, this, match[1] 未执行此转换 - 未创建来自 match[1] 的 string,thread 作为第三个参数 sub_match ,然后在线程内部它使用它来获取string。但是,将sub_match 中的数据正确读取到新字符串中可能为时已晚。 Here is small example 显示了这种行为。
【解决方案2】:

来自some docs:

因为std::match_results 拥有std::sub_matches,每个都是匹配的原始字符序列的一对迭代器,如果原始字符序列被破坏或迭代器无效,则检查std::match_results 是未定义的行为其他原因。

...同一页告诉我们std::smatch 是std::match_results&lt;std::string::const_iterator&gt; 的别名。

您需要获取这些迭代器引用的字符范围的副本,并将其传递给std::thread。

确实thread_func 在参数转换期间已经在执行此复制(因为该函数采用const std::string&amp;,而不是std::sub_match),但是this occurs on the thread 到那时为时已晚,因为您的指针已经[可能]悬而未决。

【讨论】:

  • thread_func的参数是const std::string&amp;,所以不会创建一个隐式的std::string临时吗?
  • @xskxzr 是的,但我相信这发生在新线程上,即 [可能] 为时已晚 (ref)
  • 哈,从字面上看,自从我们的互动以来,我完全随机/偶然地在我的代码中发现了一个std::smatch 悬空指针错误:D
  • @SombreroChicken 创造工作保障是没有错的:D
猜你喜欢
  • 2016-03-13
  • 1970-01-01
  • 2020-12-15
  • 2017-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-16
  • 2017-03-22
相关资源
最近更新 更多