Что не так при передаче std :: sub_match в качестве аргумента в std :: thread? - PullRequest
5 голосов
/ 10 июля 2019

Я передаю std::sub_match в качестве аргумента std::thread (см. Мой пример кода ниже). Функция потока ожидает константную ссылку на строку. 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 ]

5 голосов
/ 10 июля 2019

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 передается в поток.

4 голосов
/ 10 июля 2019

С некоторые документы :

Поскольку std::match_results содержит std::sub_matches, каждый из которых представляет собой пару итераторов в исходную последовательность символов, которая была сопоставлена, это неопределенное поведение для проверки std::match_results, была ли исходная последовательность символов уничтожена или итераторы к ней были признаны недействительными по другим причинам.

& hellip; и та же страница учит нас, что std::smatch является псевдонимом для std::match_results<std::string::const_iterator>.

Вам потребуется взять копию диапазона символов, на который ссылаются эти итераторы, и передать его в std::thread.

Это правда, что thread_func уже будет делать эту копию во время преобразования аргумента (поскольку функция принимает const std::string&, а не std::sub_match), но это происходит в потоке к этому моменту уже слишком поздно, так как ваши указатели уже [потенциально] висят.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...