كيفية الانضمام إلى الخيط الذي هو معلق على منع IO؟

StackOverflow https://stackoverflow.com/questions/203754

سؤال

ولدي موضوع قيد التشغيل في الخلفية أن يقرأ الأحداث من جهاز إدخال بطريقة الحجب، والآن عندما كنت الخروج من التطبيق أريد لتنظيف موضوع بشكل صحيح، ولكن لا أستطيع أن مجرد تشغيل pthread_join () لأن موضوع من شأنه أبدا الخروج بسبب IO الحجب.

وكيف يمكنني إيجاد حل سليم هذا الوضع؟ يجب أن أبعث pthread_kill (theard، SIGIO) أو pthread_kill (theard، SIGALRM) لكسر كتلة؟ إما من أنه حتى الإشارة الصحيحة؟ أم أن هناك طريقة أخرى لحل هذا الوضع وترك هذا الخروج موضوع الطفل حجب قراءتها؟

وحيرة حاليا قليلا لأن أيا من بلدي غوغلينغ تحول ما يصل إلى حل.

وهذا هو على لينكس واستخدام بثريدس.

تحرير: لقد لعبت في جميع أنحاء قليلا مع SIGIO وSIGALRM، عندما لا تثبيت معالج إشارة أنها كسر IO عرقلة، ولكن يعطي رسالة على حدة ( "I / O ممكن")، ولكن عندما أقوم بتثبيت معالج إشارة، لتجنب هذه الرسالة، أنها لم تعد كسر IO عرقلة، لذلك لا ينهي الموضوع. لذلك أنا نوع من العودة إلى خطوة واحدة.

هل كانت مفيدة؟

المحلول

وقديم السؤال الذي من الممكن جدا أن تحصل على إجابة جديدة وتطورت الأمور والتكنولوجيا الجديدة متاحة الآن ل<م> أفضل إشارات مقبض في المواضيع.

ومنذ نواة لينكس 2.6.22، ونظام يوفر وظيفة جديدة تسمى signalfd() والتي يمكن استخدامها لفتح واصف ملف لمجموعة معينة من الاشارات يونكس (خارج تلك التي تقتل فورا عملية).

// 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 signal
f_socket = signalfd(-1, &set, SFD_NONBLOCK | SFD_CLOEXEC);

والآن يمكنك استخدام وظائف poll() أو select() للاستماع إلى إشارة على طول أكثر اصف ملف المعتاد (مأخذ، ملف على القرص، الخ) التي تم الاستماع على.

ووNONBLOCK أمر مهم إذا كنت ترغب في حلقة التي يمكن أن تحقق الإشارات وغيرها من واصفات ملف مرارا وتكرارا (أي أنه من المهم أيضا على جهاز واصف ملف آخر).

ولدي مثل هذا التنفيذ التي تعمل مع (1) توقيت، (2) مآخذ، (3) أنابيب (4) إشارات يونكس، (5) ملفات العادية. في الواقع، حقا أي واصف ملف زائد توقيت.

https://github.com /m2osw/snapcpp/blob/master/snapwebsites/libsnapwebsites/src/snapwebsites/snap_communicator.cpp
https://github.com/m2osw/ snapcpp / فقاعة / الماجستير / snapwebsites / libsnapwebsites / SRC / snapwebsites / snap_communicator.h

وأنت قد تكون مهتمة ايضا من قبل المكتبات مثل libevent

نصائح أخرى

والطريقة المتعارف عليها للقيام بذلك هي مع pthread_cancel، حيث قامت به موضوع pthread_cleanup_push / pop لتوفير تنظيف لأي ومن استخدام الموارد.

للأسف هذا لا يمكن أن تستخدم في C ++ الرمز، من أي وقت مضى. أي C ++ الأمراض المنقولة جنسيا ليب الرمز، أو أي try {} catch() على المكدس داعيا في الوقت من pthread_cancel من المحتمل segvi قتل العملية بأكملها.

والحل الوحيد هو التعامل مع SIGUSR1، ووضع العلم توقف، pthread_kill(SIGUSR1)، ثم في أي مكان يتم حظر موضوع على I / O، إذا كنت تحصل على EINTR تحقق العلم توقف قبل إعادة وI / O. في الممارسة العملية، وهذا لا ينجح دائما على لينكس، لا أعرف لماذا.

ولكن على أي حال فإنه من العبث أن نتحدث عن إذا كان لديك لاستدعاء أي ليب 3rd الطرف، لأنها من المرجح أن يكون حلقة ضيقة أن إعادة تشغيل ببساطة I / O على EINTR. الهندسة العكسية واصف ملف لإغلاقه لن تنخفض إما أنها يمكن أن تنتظر على إشارة أو الموارد الأخرى. في هذه الحالة، فمن المستحيل لكتابة رمز العمل، الفترة. نعم، وهذا هو تماما وتلف الدماغ. التحدث إلى اللاعبين الذين صمموا C ++ الاستثناءات وpthread_cancel. يفترض هذا قد تكون ثابتة في بعض النسخة المقبلة من C ++. حظا سعيدا مع ذلك.

وأنا أيضا أن أوصي باستخدام مختارة أو بعض وسائل أخرى غير المستندة إلى إشارة لإنهاء موضوع الخاص بك. أحد الأسباب لدينا المواضيع هو محاولة والابتعاد عن الجنون إشارة. وقال ...

وعموما واحد يستخدم pthread_kill () مع SIGUSR1 أو SIGUSR2 لإرسال إشارة إلى الموضوع. الإشارات الأخرى المقترحة - SIGTERM، SIGINT، SIGKILL - لها دلالات واسعة العملية التي قد لا تكون مهتمة في

.

وأما عن السلوك عند إرسال إشارة، تخميني هو أن لديها علاقة مع الطريقة التي تعاملت مع الإشارة. إذا كان لديك أي معالج تثبيت، يتم تطبيق الإجراء الافتراضي من هذه الإشارة، ولكن في سياق الترابط الذي تلقى إشارة. حتى SIGALRM، على سبيل المثال، سيتم "التعامل" من موضوع، ولكن ستتألف التعامل مع إنهاء عملية - ربما لا يكون السلوك المطلوب

واستلام إشارة من موضوع وكسر عموما من قراءة مع EINTR، إلا إذا كان هو حقا في تلك الدولة غير المنقطعة كما ورد في إجابة سابقة. ولكن اعتقد انها ليست، أو أن تجاربك مع SIGALRM وSIGIO لم إنهاء العملية.

هل قراءة بك ربما في نوع من حلقة؟ إذا ينهي القراءة مع -1 العودة، ثم الخروج من تلك الحلقة والخروج من الموضوع.

ويمكنك ان تلعب مع هذا الرمز قذرة جدا لقد وضعت لاختبار الفرضيات بلدي - أنا بضعة من المناطق الزمنية بعيدا عن الكتب POSIX لي في لحظة ...

#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() لديك يمكن أن يكون له مهلة، حتى لو كان نادرا، وذلك للخروج من موضوع برشاقة على حالة معينة. وأنا أعلم، تمتص الاقتراع ...

وثمة بديل آخر هو أن يكون أنبوب لكل طفل وأضيف أن على قائمة واصفات الملفات التي يتابعها الموضوع. إرسال البايت إلى الأنابيب من الوالد عندما تريد ذلك الطفل للخروج. لا اقتراع على حساب أنبوب في الموضوع.

ويعتمد كيف انها تنتظر IO.

وإذا كان موضوع هو في حالة "غير المنقطعة IO" (كما هو موضح "D" في أعلى)، ثم هناك حقا لا شيء على الإطلاق يمكنك القيام به حيال ذلك. عادة المواضيع فقط دخول هذه الدولة لفترة وجيزة، والقيام شيء مثل الانتظار لصفحة ليتم تبديل في (أو الطلب تحميل، على سبيل المثال من ملف mmap'd أو مكتبة الخ المشتركة)، ومع ذلك فشل (وخاصة من خادم NFS) يمكن أن تسبب لها بالبقاء في تلك الولاية لفترة أطول.

وهو حقا هناك وسيلة للهروب من هذه الحالة "D". فإن موضوع لا تستجيب للإشارات (يمكنك إرسالها، لكنها لن تكون قائمة الانتظار).

إذا انها وظيفة IO العادية مثل قراءة ()، الكتابة () أو وظيفة الانتظار مثل تحديد () أو استطلاع ()، ستسلم الإشارات بشكل طبيعي.

وكان أحد الحلول التي وقعت لي في المرة الأخيرة كان لي قضية مثل هذه لإنشاء ملف (على سبيل المثال. أنبوب) التي كانت موجودة فقط لغرض الاستيقاظ المواضيع الحجب.

والفكرة هي إنشاء ملف من الحلقة الرئيسية (أو 1 في موضوع، كما تقترح مهلة - وهذا من شأنه أن يعطي لك السيطرة الدقيقة على التي استيقظ المواضيع). جميع المواضيع التي يتم حظر على ملف I / سوف O القيام تحديد ()، وذلك باستخدام ملف (ق) أنها تحاول أن تعمل على، فضلا عن الملف الذي تم إنشاؤه من قبل حلقة رئيسية (كعضو في القراءة ملف مجموعة اصف). وهذا ينبغي جعل كل من تحديد () يدعو العودة.

وسوف تحتاج إلى أن تضاف إلى كل من المواضيع كود للتعامل مع هذا "الحدث" من الحلقة الرئيسية.

إذا حلقة رئيسية بحاجة إلى الاستيقاظ كل المواضيع أنه إما أن الكتابة إلى الملف أو إغلاقه.


لا أستطيع أن أقول على وجه اليقين ما إذا كان هذا يعمل، وإعادة هيكلة يعني أن الحاجة إلى محاولة اختفت.

وأعتقد، كما قلت، فإن السبيل الوحيد يتمثل في إرسال إشارة ثم قبض والتعامل معها بشكل مناسب. قد تكون البدائل SIGTERM، SIGUSR1، SIGQUIT، SIGHUP، SIGINT، وما إلى ذلك.

ويمكنك أيضا استخدام حدد () على واصف المدخلات الخاصة بك حتى يتسنى لك قراءة فقط عندما يكون جاهزا. هل يمكن استخدام حدد () مع مهلة، ويقول، ثانية واحدة ثم معرفة ما اذا كان هذا موضوع يجب أن ينتهي.

وأنا دائما إضافة "<م> قتل " وظيفة تتعلق وظيفة الخيط الذي أركض قبل الانضمام يضمن فإن موضوع يكون joinable خلال فترة زمنية معقولة. عندما موضوع يستخدم حظر IO أحاول الاستفادة من نظام لكسر القفل. على سبيل المثال، عند استخدام مأخذ كنت أود أن يكون دعوة القتل <م> اغلاق (2) أو <م> قريب (2) عليه الذي من شأنه أن يتسبب في كومة شبكة لإنهاء كما كان يتمنى.

وتنفيذ مأخذ لينكس هو الخيط آمنة.

وأنا مندهش أن أحدا قد اقترح pthread_cancel. أنا كتبت مؤخرا برنامج متعدد الخيوط I / O والدعوة إلغاء () وانضمام () بعد ذلك عملت كبيرة فقط.

وكنت قد حاولت أصلا pthread_kill () ولكن انتهى بي الأمر مجرد إنهاء البرنامج كامل مع إشارات اختبرت معها.

إذا كنت عرقلة في مكتبة طرف ثالث حلقات على EINTR، قد ترغب في النظر في الجمع بين استخدام pthread_kill مع إشارة (USR1 الخ) استدعاء وظيفة فارغة (لا SIG_IGN) مع إغلاق الواقع / استبدال ملف واصف في السؤال. باستخدام dup2 لتحل محل فد مع / ديف / لاغية أو ما شابه ذلك، فسوف تتسبب في مكتبة لجهة خارجية للحصول على نتيجة نهاية الملف عند ذلك إعادة القراءة.

لاحظ أن من الحزب الاتحادي الديمقراطي () جي مأخذ الأصلي أولا، يمكنك تجنب الحاجة إلى إغلاق فعلا المقبس.

والإشارات وموضوع مشكلة خفية على لينكس وفقا لرجل صفحات مختلفة. هل تستخدم LinuxThreads، أو NPTL (إذا كنت على لينكس)؟

وأنا لست متأكدا من هذا، ولكن أعتقد أن معالج إشارة يؤثر على العملية برمتها، لذلك إما أن تستمر إنهاء العملية برمتها أو كل شيء.

ويجب أن تستخدم توقيت حدد أو الاستقصاء، وتعيين إشارة العالمي لإنهاء موضوع الخاص بك.

وأعتقد أن نهج أنظف لديهم موضوع استخدام المتغيرات الشرطية في حلقة للتواصل.

وعندما أطلقت حدث ط / س، الشرطي يجب إشارية.

والخيط الرئيسي يمكن أن مجرد إشارة لحالة بينما chaning والمسند حلقة إلى false.

وشيء من هذا القبيل:

while (!_finished)
{
    pthread_cond_wait(&cond);
    handleio();
}
cleanup();

وحفظ مع المتغيرات الشرطية للتعامل مع الإشارات بشكل صحيح. ويمكن أن يكون أشياء مثل "wakeups زائفة". لذلك أود أن التفاف الدالة الخاصة حول وظيفة cond_wait.

struct pollfd pfd;
pfd.fd = socket;
pfd.events = POLLIN | POLLHUP | POLLERR;
pthread_lock(&lock);
while(thread_alive)
{
    int ret = poll(&pfd, 1, 100);
    if(ret == 1)
    {
        //handle IO
    }
    else
    {
         pthread_cond_timedwait(&lock, &cond, 100);
     }
}
pthread_unlock(&lock);

وthread_alive هو متغير موضوع معين والتي يمكن استخدامها في تركيبة مع إشارة لقتل الموضوع.

وبالنسبة للIO مقبض الباب كنت بحاجة للتأكد من أنك تستخدم مفتوح مع خيار O_NOBLOCK، أو إذا كان من مأخذ هناك علم مماثل يمكنك ضبط MSG_NOWAIT ؟؟. لقوات الدفاع والأمن الأخرى التراسل الفوري غير متأكد

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top