GNU/Linux >> Belajar Linux >  >> Linux

Bagaimana cara bergabung dengan utas yang menggantung di pemblokiran IO?

Saya juga akan merekomendasikan menggunakan pilih atau cara lain berbasis non-sinyal untuk mengakhiri utas Anda. Salah satu alasan kami memiliki utas adalah untuk mencoba dan menjauh dari kegilaan sinyal. Konon...

Umumnya seseorang menggunakan pthread_kill() dengan SIGUSR1 atau SIGUSR2 untuk mengirim sinyal ke utas. Sinyal lain yang disarankan--SIGTERM, SIGINT, SIGKILL--memiliki semantik seluruh proses yang mungkin tidak Anda minati.

Adapun perilaku saat Anda mengirim sinyal, tebakan saya berkaitan dengan cara Anda menangani sinyal. Jika Anda tidak menginstal penangan, tindakan default dari sinyal tersebut diterapkan, tetapi dalam konteks utas yang menerima sinyal. Jadi SIGALRM, misalnya, akan "ditangani" oleh utas Anda, tetapi penanganannya terdiri dari penghentian proses--mungkin bukan perilaku yang diinginkan.

Penerimaan sinyal oleh utas umumnya akan menghentikannya dari pembacaan dengan EINTR, kecuali jika benar-benar dalam keadaan tidak terputus seperti yang disebutkan dalam jawaban sebelumnya. Tapi menurut saya tidak, atau eksperimen Anda dengan SIGALRM dan SIGIO tidak akan menghentikan prosesnya.

Apakah bacaan Anda mungkin dalam semacam lingkaran? Jika pembacaan diakhiri dengan -1 kembali, keluar dari loop itu dan keluar dari utas.

Anda dapat bermain dengan kode yang sangat ceroboh ini yang saya kumpulkan untuk menguji asumsi saya--Saya berada beberapa zona waktu dari buku POSIX saya saat ini...

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

}

Pertanyaan lama yang bisa mendapatkan jawaban baru karena berbagai hal telah berkembang dan teknologi baru sekarang tersedia untuk lebih baik menangani sinyal di utas.

Sejak kernel Linux 2.6.22, sistem menawarkan fungsi baru yang disebut signalfd() yang dapat digunakan untuk membuka deskriptor file untuk serangkaian sinyal Unix tertentu (di luar sinyal yang langsung mematikan proses.)

// defined a set of signals
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
// ... you can add more than one ...

// prevent the default signal behavior (very important)
sigprocmask(SIG_BLOCK, &set, nullptr);

// open a file descriptor using that set of Unix signals
f_socket = signalfd(-1, &set, SFD_NONBLOCK | SFD_CLOEXEC);

Sekarang Anda dapat menggunakan poll() atau select() berfungsi untuk mendengarkan sinyal di sepanjang deskriptor file yang lebih umum (soket, file di disk, dll.) yang sedang Anda dengarkan.

NONBLOCK penting jika Anda menginginkan loop yang dapat memeriksa sinyal dan deskriptor file lainnya berulang kali (artinya, ini juga penting pada deskriptor file Anda yang lain).

Saya memiliki implementasi yang bekerja dengan (1) pengatur waktu, (2) soket, (3) pipa, (4) sinyal Unix, (5) file biasa. Sebenarnya, semua deskriptor file plus timer.

https://github.com/m2osw/snapcpp/blob/master/snapwebsites/libsnapwebsites/src/snapwebsites/snap_communicator.cpp
https://github.com/m2osw/snapcpp/blob/master/snapwebsites/libsnapwebsites/src/snapwebsites/snap_communicator.h

Anda mungkin juga tertarik dengan perpustakaan seperti libevent


Cara kanonis untuk melakukannya adalah dengan pthread_cancel , di mana utas telah melakukan pthread_cleanup_push /pop untuk menyediakan pembersihan untuk setiap sumber daya yang digunakannya.

Sayangnya ini TIDAK bisa digunakan dalam kode C++, selamanya. Kode lib std C++ apa pun, atau try {} catch() APAPUN pada tumpukan panggilan pada saat pthread_cancel akan berpotensi mematikan seluruh proses Anda.

Satu-satunya solusi adalah menangani SIGUSR1 , menyetel bendera berhenti, pthread_kill(SIGUSR1) , lalu di mana pun utas diblokir di I/O, jika Anda mendapatkan EINTR periksa bendera berhenti sebelum mencoba kembali I/O. Dalam praktiknya, ini tidak selalu berhasil di Linux, tidak tahu kenapa.

Tetapi bagaimanapun juga, tidak ada gunanya membicarakan jika Anda harus memanggil lib pihak ketiga mana pun, karena kemungkinan besar mereka akan memiliki loop ketat yang hanya memulai ulang I/O pada EINTR . Merekayasa balik deskriptor file mereka untuk menutupnya juga tidak akan berhasil—mereka mungkin menunggu di semafor atau sumber daya lainnya. Dalam hal ini, tidak mungkin menulis kode yang berfungsi, titik. Ya, ini benar-benar merusak otak. Bicaralah dengan orang-orang yang merancang pengecualian C++ dan pthread_cancel . Seharusnya ini dapat diperbaiki di beberapa versi C++ yang akan datang. Semoga berhasil dengan itu.


select() Anda dapat memiliki batas waktu, meskipun jarang, untuk keluar dari utas dengan anggun pada kondisi tertentu. Saya tahu, pemungutan suara itu menyebalkan...

Alternatif lain adalah memiliki pipa untuk setiap anak dan menambahkannya ke daftar deskriptor file yang diawasi oleh utas. Kirim byte ke pipa dari induk saat Anda ingin anak itu keluar. Tidak ada polling dengan biaya satu pipa per utas.


Linux
  1. SSHFS? Apa itu dan bagaimana cara menggunakannya?

  2. Bagaimana Memberi Sinyal Akhir Dari Input Stdin?

  3. Cara mencetak pthread_t

  1. Bagaimana cara mengatur nama utas di Linux pthreads?

  2. Bagaimana cara menghindari penggunaan printf dalam penangan sinyal?

  3. Bagaimana cara mendapatkan id utas dari pthread di program linux c?

  1. Bagaimana cara mengirim sinyal ke program yang dijalankan dalam wadah buruh pelabuhan?

  2. Bagaimana cara menggabungkan dua file CSV?

  3. Bagaimana cara menggabungkan/menggabungkan banyak file mp3?