将std :: sub_match作为参数传递给std :: thread时出了什么问题?

时间:2019-07-10 11:56:24

标签: c++ multithreading c++11

我将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"

2 个答案:

答案 0 :(得分:5)

operator[]

std::smatch返回sub_match,可以将其视为匹配字符的一对迭代器。

在调用regex_match之后,只要存在operator[],就可以使用event访问子匹配项。删除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传递给了线程。

答案 1 :(得分:4)

来自some docs

  

由于std::match_results持有std::sub_matches,每个std::match_results都是一对与原始字符序列匹配的迭代器,因此检查std::smatch是否破坏了原始字符序列是未定义的行为或它的迭代器由于其他原因而无效。

…并且同一页告诉我们std::match_results<std::string::const_iterator>std::thread的别名。

您需要复制这些迭代器引用的字符范围,并将其传递给thread_func

的确,const std::string&已经在参数转换期间进行了此复制(因为该函数采用的是std::sub_match,而不是user1),而是this occurs on the thread指出为时已晚,因为您的指针已经[潜在地]晃来晃去。