2016-11-26 9 views
2

私はC++を初めて使用しています。なぜ次のコードにセグメンテーションフォールトがあるのか​​わかりません。 Doo()は、マップ<>を含むクラスです。 Doo :: start()を呼び出してwhileループスレッドを開始することができます。その後、Doo :: turnoff()を呼び出してスレッドを終了します。自分のコードに何が間違っているのか分かりません。私の理解を助けてください。次のコマンドでコンパイルさセグメンテーションフォルト、考えられる理由:関数ポインタ、マルチスレッド、stlマップなど

#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; 
} 

I:それは問題なくコンパイル

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) 
+1

ルールが破られることはありませんが、その前にルールが存在することを確認することをお勧めします。 [C++識別子でアンダースコアを使用する際の規則は何ですか?](http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-ac-identifier) – user4581301

+1

あなたは1秒の睡眠が早い1秒の睡眠の後に終了すると仮定しているようですが、それは保証されていません。 – Dani

答えて

-1

問題がthread m_thrでラムダです。参照によってfptrをキャプチャしていますが、関数が実行されると、この参照は破棄されます。しかしスレッドは、セグメンテーション違反のためにfptrを破壊して死に至るという参照を使用しようとします。

解決策は単純です - 参考値ではなく値でfptrをキャプチャしてください。

thread m_thr([=](){ // changed & to = 
    while(!_turnoff){ 
     this_thread::sleep_for(chrono::seconds(1)); 
     fptr(msg); 
    } 
}); 
+0

参照が参照するオブジェクトは、関数の実行が終了すると_not_破壊されます。それは '斗'のメンバーであり、 '斗'のインスタンスがそうである限り生き続けるだろう。 – Snps

+0

値でキャプチャすると実際には参照が邪魔になることはありませんが、なぜ間違っているのか理解しています。 – Snps

0

最初に、_turnoffにデータ競争があるので、あなたの例ではUBが表示されます。 はありません。はデフォルトではC++ではアトミックなので、std::atomicを使用したり、同期したりすることをお勧めします。


次に、あなたが参照している問題は、メインスレッドによって割り当てられた自動オブジェクト(Doo::msg)に参照を取るスレッドタスクから生じます。ワーカースレッドがであるので、メインスレッドのハンドルからをデタッチします。これは、メインスレッドがワーカースレッドより先に終了し、スタック上の割り当てられたオブジェクトをすべて破棄できることを意味します(主スタック自体を破棄します)。これにより、ワーカースレッドが破棄されたオブジェクトへの参照を保持する可能性があります。

メインでターンオフ信号を送信した後は、ワーカー/スレッドと同じ時間だけワーカー/スレッドのループで待機/スリープします。その結果、ぶら下がっている参照を使用してワーカースレッドが「最後に終了する」可能性が高くなります。

これは通常プログラムが終了するため問題はありませんが、Valgrindは依然として不平を言います。

関連する問題