4

作業中のマルチスレッドWindowsサーバーがあり、control-cを使用してプログラムを閉じているときに特定の基準セットが発生すると、クラッシュすることがわかりました。サーバーがクライアントからパケットを受信し、control-cを使用すると、サーバーがクラッシュします。サーバーを起動し、パケットを一定時間待機してからcontrol-cを使用すると、サーバーは正常に終了します。

ただし、奇妙なことに、プログラムが例外をスローした場合でも(正常でない限り)、すべてのスレッドがステータス0で終了していると報告しています。

First-chance exception at 0x75A16DA7 (kernel32.dll) in server.exe: 0x40010005: Control-C.
HEAP[server.exe]: HEAP: Free Heap block 96a818 modified at 96a908 after it was freed
server.exe has triggered a breakpoint.
The thread 0xc34 has exited with code 0 (0x0).
The thread 0x1c64 has exited with code 0 (0x0).
The thread 0xdbc has exited with code 0 (0x0).
The thread 0x117c has exited with code 0 (0x0).
The thread 0x1444 has exited with code 0 (0x0).
The thread 0x1d60 has exited with code 0 (0x0).
The thread 0x798 has exited with code 0 (0x0).
The thread 0x700 has exited with code 0 (0x0).
The thread 0x1bbc has exited with code 0 (0x0).
The thread 0x1b74 has exited with code 0 (0x0).
The program '[7528] server.exe' has exited with code 0 (0x0).

この問題の原因と思われるコードの部分:

void handleSignal(int sig) {
    std::unique_lock<std::mutex> lock(signalMutex); // <-- comment out and it doesn't crash
    signaled = true;
    _receivedSignal = sig;
    signalHandlerCondition.notify_one(); // <-- comment out and it doesn't crash
}

ミューテックス変数と条件変数はどちらもグローバルです。

std::mutex signalMutex;
std::condition_variable signalHandlerCondition;

そのイベントによって通知されたときにサーバーを正常にシャットダウンしようとする専用の信号処理スレッドがあります。

void run() {
    while (gContinueRunning && _continueRunning) {
        std::unique_lock<std::mutex> lock(signalMutex);
        signalHandlerCondition.wait(lock);
        if (signaled) {
            gContinueRunning = false;
            signaled = false;
            Server::stop();
        }
    }
}

確かに、問題のある行をコメントアウトすると、プログラムは信号にまったく応答しません。シグナル処理ループに新しいシグナルがあることを通知する必要がないように、wait_forを設定することもできますが、それが最善の方法ではないと思います。

シグナルについてMSDNから何かを読みました:

CTRL + C割り込みが発生すると、Win32オペレーティングシステムはその割り込みを具体的に処理するための新しいスレッドを生成します。

シグナルハンドラルーチンは通常、割り込みが発生すると非同期に呼び出されるため、実行時の操作が不完全で不明な状態の場合、シグナルハンドラ関数が制御を取得する場合があります。

この場合、それが当てはまるかどうかは正直わかりません。もしそうなら、それはシグナルハンドラーが呼び出されたときに私のミューテックスが存在するかもしれないし存在しないかもしれないことを意味しますか?

では、信号にアプローチするための最良の方法は何ですか?ここでどのような問題が発生していますか?


編集:いくつかのことを片付けるためだけに:

void start() {
    _receivedSignal = 0;
    _continueRunning = true;
    // start thread
    std::thread signalHandlerThread(run);
    _signalHandlerThread = std::move(signalHandlerThread);

    // register signals
    signal(SIGABRT, SignalHandler::handleSignal);
    signal(SIGTERM, SignalHandler::handleSignal);
    signal(SIGINT,  SignalHandler::handleSignal);
}

ミューテックスを削除した後でも、プログラムは少し進んでいるように見えますが、メインが終了するまでです。

msvcr110d.dll!operator delete(void * pUserData) Line 52 C++
server.exe!std::_Ref_count<User>::_Destroy() Line 161   C++
server.exe!std::_Ref_count_base::_Decref() Line 120 C++
server.exe!std::_Ptr_base<User>::_Decref() Line 347 C++
server.exe!std::shared_ptr<User>::~shared_ptr<User>() Line 624  C++
server.exe!std::pair<unsigned int const ,std::shared_ptr<User> >::~pair<unsigned int const ,std::shared_ptr<User> >()   C++
server.exe!std::pair<unsigned int const ,std::shared_ptr<User> >::`scalar deleting destructor'(unsigned int)    C++
server.exe!std::allocator<std::_Tree_node<std::pair<unsigned int const ,std::shared_ptr<User> >,void *> >::destroy<std::pair<unsigned int const ,std::shared_ptr<User> > >(std::pair<unsigned int const ,std::shared_ptr<User> > * _Ptr) Line 624   C++
server.exe!std::allocator_traits<std::allocator<std::_Tree_node<std::pair<unsigned int const ,std::shared_ptr<User> >,void *> > >::destroy<std::pair<unsigned int const ,std::shared_ptr<User> > >(std::allocator<std::_Tree_node<std::pair<unsigned int const ,std::shared_ptr<User> >,void *> > & _Al, std::pair<unsigned int const ,std::shared_ptr<User> > * _Ptr) Line 758 C++
server.exe!std::_Wrap_alloc<std::allocator<std::_Tree_node<std::pair<unsigned int const ,std::shared_ptr<User> >,void *> > >::destroy<std::pair<unsigned int const ,std::shared_ptr<User> > >(std::pair<unsigned int const ,std::shared_ptr<User> > * _Ptr) Line 909    C++
server.exe!std::_Tree<std::_Tmap_traits<unsigned int,std::shared_ptr<User>,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::shared_ptr<User> > >,0> >::_Erase(std::_Tree_node<std::pair<unsigned int const ,std::shared_ptr<User> >,void *> * _Rootnode) Line 2069 C++
server.exe!std::_Tree<std::_Tmap_traits<unsigned int,std::shared_ptr<User>,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::shared_ptr<User> > >,0> >::clear() Line 1538   C++
server.exe!std::_Tree<std::_Tmap_traits<unsigned int,std::shared_ptr<User>,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::shared_ptr<User> > >,0> >::erase(std::_Tree_const_iterator<std::_Tree_val<std::_Tree_simple_types<std::pair<unsigned int const ,std::shared_ptr<User> > > > > _First, std::_Tree_const_iterator<std::_Tree_val<std::_Tree_simple_types<std::pair<unsigned int const ,std::shared_ptr<User> > > > > _Last) Line 1512    C++
server.exe!std::_Tree<std::_Tmap_traits<unsigned int,std::shared_ptr<User>,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::shared_ptr<User> > >,0> >::_Tidy() Line 2216   C++
server.exe!std::_Tree<std::_Tmap_traits<unsigned int,std::shared_ptr<User>,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::shared_ptr<User> > >,0> >::~_Tree<std::_Tmap_traits<unsigned int,std::shared_ptr<User>,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::shared_ptr<User> > >,0> >() Line 1190 C++
server.exe!std::map<unsigned int,std::shared_ptr<User>,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::shared_ptr<User> > > >::~map<unsigned int,std::shared_ptr<User>,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::shared_ptr<User> > > >() C++
server.exe!`dynamic atexit destructor for 'User::_usersListBySession''()    C++
msvcr110d.dll!doexit(int code, int quick, int retcaller) Line 584   C
msvcr110d.dll!exit(int code) Line 394   C
server.exe!__tmainCRTStartup() Line 549 C
server.exe!mainCRTStartup() Line 377    C

他のすべてのスレッドがなくなったようです。私はおそらく他の場所で間違いを犯したと思います。

信号機能の安全性をクリアしてくれてありがとう。


編集2:無関係の共有ポインタが問題を引き起こしているようです!でも、これから何か良いものが出てきて嬉しいです。

編集3:まったく関係のない問題がクラッシュを引き起こしていました。しかし、今ではすべてが世界で良いです。

4

1 に答える 1

2

デバッガーが Ctrl-C イベントを処理しているため、これが発生していると思われます。

このMSDN の記事には、次のように書かれています。

コンソール プロセスがデバッグされており、CTRL+C シグナルが無効になっていない場合、システムは DBG_CONTROL_C 例外を生成します。この例外は、デバッガーの利益のためにのみ発生し、アプリケーションは例外ハンドラーを使用して処理しないでください。デバッガーが例外を処理する場合、アプリケーションは CTRL+C を認識しませんが、例外が 1 つあります。アラート可能な待機は終了します。デバッガーが未処理で例外を渡すと、前述のように、CTRL + C がコンソール プロセスに渡され、シグナルとして扱われます。

イベント フィルタを「出力 - 処理しない」に設定して、アプリケーションがこれを処理できるようにすることができます。WinDbg でこれを設定する方法のスクリーン ショーを添付しました。Visual Studio はこれを「Win32 Exceptions」の下に一覧表示します。

WinDBG イベント フィルター

編集: また、イベント ハンドラーでミューテックスをロックしようとすることは悪い習慣と見なされることを追加する必要があります。シグナル ハンドラが呼び出されたときにミューテックスがすでに取得されている場合、アプリケーションはシグナル ハンドラが完了するまで再開できず、ミューテックスが取得されるまでシグナル ハンドラは完了できないため、デッドロックが発生します。これはあなたのユースケースではありそうにありませんが、偽のウェイクアップ中の CTRL-Cまたは 2 つの CTRL-C の背中合わせは、デッドロックを引き起こす可能性があります。

于 2012-11-26T20:52:22.250 に答える