6

私はこの男と同じ状況にいますが、答えがよくわかりません。

問題:

  • スレッド 1 はaccept、ブロックしているソケットを呼び出します。
  • closeこのソケットでスレッド 2 を呼び出します。
  • スレッド 1 はブロックし続けます。受け入れから戻ってほしい。

ソリューション:

あなたがすべきことは、受け入れでブロックされているスレッドにシグナルを送ることです。これにより EINTR が与えられ、きれいに解放され、ソケットを閉じることができます。それを使用しているスレッド以外から閉じないでください。

ここで何をすべきかわかりません-シグナルがスレッド1で受信され、acceptすでにブロックされており、シグナルハンドラーが終了した後も引き続きブロックされます。

  1. 答えは、私が何をすべきかを本当に意味していますか?
  2. スレッド 1 のシグナル ハンドラーがacceptすぐに復帰するような処理を実行できる場合、スレッド 2 はシグナルなしで同じ処理を実行できないのはなぜでしょうか?
  3. 信号なしでこれを行う別の方法はありますか? ライブラリの警告を増やしたくありません。
4

5 に答える 5

5

でブロックする代わりに、 、 、または類似の呼び出しの 1 つをaccept()ブロックして、複数のファイル記述子でのアクティビティを待機し、「セルフパイプ トリック」を使用できるようにします。に渡されるすべてのファイル記述子は、非ブロッキング モードである必要があります。ファイル記述子の 1 つは、で使用するサーバー ソケットにする必要があります。それが読み取り可能になったら、先に進んで電話する必要があり、ブロックされません。それに加えて、 を作成し、ノンブロッキングに設定し、読み取り側が読み取り可能になることを確認します。他のスレッドでサーバー ソケットを呼び出す代わりに、パイプの書き込み側の最初のスレッドに 1 バイトのデータを送信します。実際のバイト値は問題ではありません。目的は、単に最初のスレッドを起動することです。いつselect()poll()select()accept()accept()pipe()close()select()パイプが読み取り可能であることを示し、read()パイプ、サーバー ソケットからのデータを無視し、close()新しい接続の待機を停止します。

于 2013-05-07T21:09:38.893 に答える
2

接続が受け入れられる前にシグナルが捕捉された場合、accept() 呼び出しはエラー コード EINTR を返します。そのため、戻り値とエラー コードを確認し、それに応じてソケットを閉じます。

シグナル メカニズムを完全に回避したい場合は、select() を使用して、accept() を呼び出す前に、受け入れる準備ができている着信接続があるかどうかを判断します。select() 呼び出しは、タイムアウトを使用して行うことができるため、回復して中止条件に対応することができます。

私は通常、終了/中止条件をチェックする while ループから 1000 ~ 3000 ミリ秒のタイムアウトで select() を呼び出します。select() が準備完了の記述子で返された場合、accept() を呼び出します。それ以外の場合は、ループして select() で再度ブロックするか、要求された場合は終了します。

于 2013-05-07T18:49:16.430 に答える
0

「ライブラリに関する警告」を増やすことなく、シグナルを使用できると思います。次の点を考慮してください。

#include <pthread.h>
#include <signal.h>
#include <stddef.h>

static pthread_t             thread;
static volatile sig_atomic_t sigCount;

/**
 * Executes a concurrent task. Called by `pthread_create()`..
 */
static void* startTask(void* arg)
{
    for (;;) {
        // calls to `select()`, `accept()`, `read()`, etc. 
    }
    return NULL;
}

/**
 * Starts concurrent task. Doesn't return until the task completes.
 */
void start()
{
    (void)pthread_create(&thread, NULL, startTask, NULL);
    (void)pthread_join(thread);
}

static void noop(const int sig)
{
    sigCount++;
}

/**
 * Stops concurrent task. Causes `start()` to return.
 */
void stop()
{
    struct sigaction oldAction;
    struct sigaction newAction;

    (void)sigemptyset(&newAction.sa_mask);
    newAction.sa_flags = 0;
    newAction.sa_handler = noop;
    (void)sigaction(SIGTERM, &newAction, &oldAction);

    (void)pthread_kill(thread, SIGTERM); // system calls return with EINTR

    (void)sigaction(SIGTERM, &oldAction, NULL); // restores previous handling

    if (sigCount > 1) // externally-generated SIGTERM was received
        oldAction.sa_handler(SIGTERM); // call previous handler

    sigCount = 0;
}

これには次の利点があります。

  • 通常の EINTR 処理以外に、タスク コードに特別なことは必要ありません。pthread_cancel()その結果、 、pthread_cleanup_push()pthread_cleanup_pop()、およびを使用するよりも、リソースのリークに関する推論が容易になりpthread_setcancelstate()ます。
  • 追加のリソース (パイプなど) は必要ありません。
  • 複数の同時タスクをサポートするように拡張できます。
  • それはかなりボイラープレートです。
  • コンパイルさえできるかもしれません。:-)
于 2015-02-21T19:18:41.627 に答える