ブロッキング IO でハングしているスレッドに参加するには?

これを行う標準的な方法は pthread_cancel を使用することです 、スレッドが pthread_cleanup_push を実行した場所 /pop 使用しているすべてのリソースのクリーンアップを提供します。

残念ながら、これは C++ コードでは使用できません。任意の C++ std lib コード、または任意の try {} catch() pthread_cancel 時の呼び出しスタック segvi によってプロセス全体が強制終了される可能性があります。

唯一の回避策は、SIGUSR1 を処理することです 、停止フラグの設定、pthread_kill(SIGUSR1) EINTR を取得した場合、I/O でスレッドがブロックされている場所 I/O を再試行する前に停止フラグを確認してください。実際には、これは Linux で常に成功するとは限りません。理由はわかりません。

ただし、サードパーティのライブラリを呼び出す必要があるかどうかについて話すのは無意味です。これは、EINTR で I/O を再起動するだけのタイトなループが発生する可能性が高いためです。 .ファイル記述子をリバース エンジニアリングして閉じることもできません。セマフォまたは他のリソースを待機している可能性があります。この場合、作業コードを書くことは単に不可能です。はい、これは完全に脳が損傷しています。 C++ 例外と pthread_cancel を設計した人たちと話してください .おそらく、これは C++ の将来のバージョンで修正される可能性があります。頑張ってください。


私も、スレッドを終了するために選択またはその他の非シグナルベースの手段を使用することをお勧めします。スレッドがある理由の 1 つは、シグナルの狂気から逃れようとすることです。そうは言っても...

通常、pthread_kill() を SIGUSR1 または SIGUSR2 と共に使用して、スレッドにシグナルを送信します。他の提案されたシグナル (SIGTERM、SIGINT、SIGKILL) には、興味のないプロセス全体のセマンティクスがあります。

シグナルを送信したときの動作については、シグナルの処理方法に関係していると思います。ハンドラーがインストールされていない場合、そのシグナルのデフォルト アクションが適用されますが、シグナルを受信したスレッドのコンテキストで適用されます。したがって、たとえば、SIGALRM はスレッドによって「処理」されますが、その処理はプロセスの終了で構成されます。おそらく、望ましい動作ではありません。

スレッドによるシグナルの受信は、以前の回答で述べたように本当に中断できない状態でない限り、通常、EINTR を使用して読み取りから抜け出します。そうでないと、SIGALRM と SIGIO を使用した実験でプロセスが終了しなかったと思います。

あなたの読書はおそらくある種のループに入っていますか?読み取りが -1 リターンで終了した場合、そのループから抜け出し、スレッドを終了します。

私の仮定をテストするためにまとめたこの非常にずさんなコードで遊んでみてください。私は現在、私の POSIX 本からタイムゾーンが 2 つ離れています...

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <signal.h>

int global_gotsig = 0;

void *gotsig(int sig, siginfo_t *info, void *ucontext) 
{
        global_gotsig++;
        return NULL;
}

void *reader(void *arg)
{
        char buf[32];
        int i;
        int hdlsig = (int)arg;

        struct sigaction sa;
        sa.sa_handler = NULL;
        sa.sa_sigaction = gotsig;
        sa.sa_flags = SA_SIGINFO;
        sigemptyset(&sa.sa_mask);

        if (sigaction(hdlsig, &sa, NULL) < 0) {
                perror("sigaction");
                return (void *)-1;
        }
        i = read(fileno(stdin), buf, 32);
        if (i < 0) {
                perror("read");
        } else {
                printf("Read %d bytes\n", i);
        }
        return (void *)i;
}

main(int argc, char **argv)
{
        pthread_t tid1;
        void *ret;
        int i;
        int sig = SIGUSR1;

        if (argc == 2) sig = atoi(argv[1]);
        printf("Using sig %d\n", sig);

        if (pthread_create(&tid1, NULL, reader, (void *)sig)) {
                perror("pthread_create");
                exit(1);
        }
        sleep(5);
        printf("killing thread\n");
        pthread_kill(tid1, sig);
        i = pthread_join(tid1, &ret);
        if (i < 0)
                perror("pthread_join");
        else
                printf("thread returned %ld\n", (long)ret);
        printf("Got sig? %d\n", global_gotsig);

}

あなたの select() 特定の条件でスレッドを正常に終了するために、まれであってもタイムアウトを設定できます。私は知っています、投票は最悪です...

もう 1 つの方法は、子ごとにパイプを用意し、それをスレッドが監視するファイル記述子のリストに追加することです。子を終了させたい場合は、親からパイプにバイトを送信します。スレッドごとにパイプを犠牲にしてポーリングする必要はありません。