2

この質問は次の質問に基づいています: Win32 で CTRL+C を処理する

Linux と Windows で実行されているマルチスレッド サーバーで作業しています。ブーストやその他のフレームワークは使用できず、std c++ のみを使用できます。

win32 側のクリーンアップ コードに問題があります。Linux 側は正常に動作しています。サーバーをシャットダウンする場合は、SIGINT(を使用してCTRL+C) を送信します。シグナル ハンドラーがグローバル変数を設定し、メイン pthread がクリーンアップ命令を実行します (他の pthread への参加、ヒープ メモリの解放など)。

Windows では、同じ動作を実現するのはそれほど単純ではないように見えます。Windows でシグナル ハンドラーがどのように動作するかを理解するための簡単なテスト プログラムを作成しました。

#include <iostream>
#include <windows.h>

bool running;

BOOL WINAPI consoleHandler(DWORD signal) {

    if (signal == CTRL_C_EVENT) {
        running = false;
        std::cout << "[CTRL+C]\n";
        return TRUE;
    }

    return FALSE;
} 

int main(int argc, char **argv) {

    running = true;

    if (!SetConsoleCtrlHandler(consoleHandler, TRUE)) {

            std::cerr << "Error: " << GetLastError() << '\n';
            return -1;
    }

    std::cout << "Main thread working hard...\n";
    while (running) { ; }

    for (int i = 0; i < 20; i++)
        std::cout << "This is the " << i << "th fake cleanup instruction\n";

    return 0;
}

出力は次のとおりです。

$ test.exe
Main thread working hard...
[CTRL+C]
This is the 0th fake cleanup instruction
This is the 1th fake cleanup instruction

そのため、メイン スレッドは 2 つの命令の後でのみすぐに強制終了されます。前の質問での提案の 1 つは、クリーンアップ コードをハンドラーに移動することでしたが、実際には役に立ちません。

ハンドラー関数が次のようになっているとします。

BOOL WINAPI consoleHandler(DWORD signal) {

    if (signal == CTRL_C_EVENT) {
        running = false;
        std::cout << "[CTRL+C]\n";

        for (int i = 0; i < 20; i++)    
            std::cout << "This is the " << i << "th fake cleanup instruction\n";

        return TRUE;
    }

    return FALSE;
} 

今、行動はさらに悪いです!出力は次のとおりです。

$ test.exe
Main thread working hard...
[CTRL+C]
This is the

MSDNによると、プロセスは常に強制終了されているようです。

HandlerRoutine は、必要なクリーンアップを実行してから、次のいずれかのアクションを実行できます。

  • プロセスを終了するには、ExitProcess 関数を呼び出します。
  • FALSE を返します。登録されたハンドラー関数のいずれも TRUE を返さない場合、デフォルトのハンドラーはプロセスを終了します。
  • TRUE を返します。この場合、他のハンドラ関数は呼び出されず、システムは終了します

プロセス。

明らかな何かが欠けていますか?win32 コンソール プロセスを終了し、そのクリーンアップ コードを実行する適切な方法は何ですか?

4

2 に答える 2

3

これは 1 つの方法ですが、イベント HANDLE と WaitForSingleObject を使用することをお勧めします。ハンドラーがインターセプトされているのを確認しながら、コアの 1 つをペグするためだけに、高速スピンループを残しました。

オプティマイザーがメインループで eval をスローすることを望まなかったので、実行中の状態をアトミックに評価してそれぞれ設定するように自由に変更しました。

#include <iostream>
#include <cstdlib>
#include <windows.h>

// using an event for monitoring
LONG running = 1;

BOOL WINAPI consoleHandler(DWORD signal)
{
    if (signal == CTRL_C_EVENT) 
    {
        std::out << "Received Ctrl-C; shutting down..." << std::endl;
        InterlockedExchange(&running, 0);
        return TRUE;
    }
    return FALSE;
} 

int main(int argc, char **argv) 
{
    if (!SetConsoleCtrlHandler(consoleHandler, TRUE)) 
    {
        std::cerr << "Error: " << GetLastError() << '\n';
        return EXIT_FAILURE;
    }

    std::cout << "Main thread working hard...\n";
    while (InterlockedCompareExchange(&running, 0, 0) == 1);

    std::cout << "Graceful shutdown received. Shutting down now." << std::endl;

    return 0;
}

出力(注:明らかでない場合に備えて、ctrl-Cを押しました)

Main thread working hard...
Received Ctrl-C; shutting down...
Graceful shutdown received. Shutting down now.

注: 64 ビット プロセスと 32 ビット プロセスの両方で、デバッグとリリースでこれをテストしましたが、問題はありません。また、VS デバッガーから実行することもできます。ハンドラーがインストールされている場合は続行できるという通知が表示されたら、[続行] を選択するだけです。

于 2013-09-13T06:53:39.273 に答える