Я новичок в C ++. Я не знаю, почему в следующем коде есть ошибка сегментации. Doo () - это класс, содержащий карту <>. Вы можете вызвать Doo :: start (), чтобы запустить поток цикла while. А затем вызовите Doo :: turnoff (), чтобы завершить поток. Я не знаю, что не так с моим кодом. Пожалуйста, помогите мне понять.

#include <iostream>
#include <thread>
#include <map>
#include <chrono>


using namespace std;

class Doo{
    int id;
    bool _turnoff=false;
    map<int,string> msg;
public:
    Doo(int _id);
    void start(bool (*fptr)(map<int,string>&));
    void turnoff();
};

Doo::Doo(int _id){
    id = _id;
    msg[1]="hello";
    msg[2]="nihao";
    msg[4]="conichiwa";
}

void Doo::start(bool (*fptr)(map<int,string>&)){
    thread m_thr([&](){
        while(!_turnoff){
            this_thread::sleep_for(chrono::seconds(1));
            fptr(msg);
        }
    });
    m_thr.detach();
}

void Doo::turnoff(){
    _turnoff=true;
}

bool hdl(map<int,string>& greet){
    cout<<greet[2]<<endl;
    return true;
}

int main(void){
    Doo d(1);
    d.start(hdl);
    while(1){
        char x;
        cin>>x;
        if(x=='q'){
            cout<<"quit"<<endl;
            d.turnoff();
            this_thread::sleep_for(chrono::seconds(1));
            break;
        }
    }
    return 0;
}

Я скомпилировал следующую команду:

g++ p3.cpp -std=c++11 -pthread

Компилируется без проблем.

Valgrind результат:

==18849== Memcheck, a memory error detector
==18849== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==18849== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==18849== Command: ./a.out
==18849== 
==18849== 
==18849== Process terminating with default action of signal 11 (SIGSEGV)
==18849==  Bad permissions for mapped region at address 0x68C1700
==18849==    at 0x68C1700: ???
==18849==    by 0x402799: void std::_Bind_simple<Doo::start(bool (*)(std::map<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<int>, std::allocator<std::pair<int const, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >&))::{lambda()#1} ()>::_M_invoke<>(std::_Index_tuple<>) (in /home/xli1989/Projects/playground/a.out)
==18849==    by 0x4026EF: std::_Bind_simple<Doo::start(bool (*)(std::map<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<int>, std::allocator<std::pair<int const, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >&))::{lambda()#1} ()>::operator()() (in /home/xli1989/Projects/playground/a.out)
==18849==    by 0x40267F: std::thread::_Impl<std::_Bind_simple<Doo::start(bool (*)(std::map<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<int>, std::allocator<std::pair<int const, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >&))::{lambda()#1} ()> >::_M_run() (in /home/xli1989/Projects/playground/a.out)
==18849==    by 0x4EF2C7F: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==18849==    by 0x53D96F9: start_thread (pthread_create.c:333)
==18849==    by 0x56F5B5C: clone (clone.S:109)
2
Xiwen Li 26 Ноя 2016 в 22:51

2 ответа

Лучший ответ

Прежде всего, есть гонка данных на _turnoff, поэтому в вашем примере будет отображаться UB. Имейте в виду, что ничто по умолчанию в C ++ не является атомарным, поэтому лучше использовать std::atomic или синхронизировать.


Теперь проблема, о которой вы говорите, возникает из-за того, что задача потока берет ссылку на автоматический объект (Doo::msg), который выделяется основным потоком. Поскольку рабочий поток отсоединен от своего дескриптора в основном потоке, это означает, что основной поток сможет завершить работу раньше рабочего потока и уничтожить все выделенные ему объекты в стеке (основной поток уничтожает собственный стек). Это приводит к тому, что рабочий поток может сохранять ссылку на уничтожаемый объект.

После отправки сигнала выключения в main вы ждете / спите столько же времени, сколько ожидание / сон в цикле рабочего потока. В результате высока вероятность того, что рабочий поток завершит работу последним, используя висящую ссылку.

Обычно это не проблема, поскольку программа все равно будет завершена, но Valgrind все равно будет жаловаться.

1
Snps 26 Ноя 2016 в 21:12

Проблема заключается в лямбде в thread m_thr. Вы захватываете fptr по ссылке, но эта ссылка уничтожается после выполнения функции. Но поток пытается использовать ссылку на уничтоженный fptr и умирает из-за ошибки сегментации.

Решение простое - фиксируйте fptr по значению, а не по ссылке.

thread m_thr([=](){ // changed & to =
    while(!_turnoff){
        this_thread::sleep_for(chrono::seconds(1));
        fptr(msg);
    }
});
-1
Starl1ght 26 Ноя 2016 в 20:11