سؤال

يرجى النظر في fork() / SIGCHLD الزائفة البرمجية التالية.

  // main program excerpt
    for (;;) {
      if ( is_time_to_make_babies ) {

        pid = fork();
        if (pid == -1) {
          /* fail */
        } else if (pid == 0) {
          /* child stuff */
          print "child started"
          exit
        } else {
          /* parent stuff */
          print "parent forked new child ", pid
          children.add(pid);
        }

      }
    }

  // SIGCHLD handler
  sigchld_handler(signo) {
    while ( (pid = wait(status, WNOHANG)) > 0 ) {
      print "parent caught SIGCHLD from ", pid
      children.remove(pid);
    }
  }

في المثال أعلاه هناك شرط السباق. فمن الممكن ل "/* child stuff */" لإنهاء قبل بدء "/* parent stuff */" الذي يمكن أن يؤدي إلى معرف المنتج الطفل إضافتها إلى قائمة الأطفال بعد انها خرجت، ولم يتم إزالتها. عندما يحين الوقت لإغلاق التطبيق إلى أسفل، فإن الوالدين الانتظار إلى ما لا نهاية للطفل بالفعل النهائي لإنهاء.

وحل واحد أستطيع أن أفكر في مواجهة هذا هو أن يكون القائمتين: started_children وfinished_children. فما استقاموا لكم فاستقيموا إضافة إلى started_children في نفس المكان أنا مضيفا إلى children الآن. ولكن في معالج إشارة، بدلا من إزالة من children كنت إضافة إلى finished_children. عند إغلاق التطبيق إلى أسفل، يمكن أن الأم مجرد الانتظار حتى الفرق بين started_children وfinished_children هو صفر.

والحلول الممكنة آخر يمكنني التفكير به هو استخدام المشتركة الذاكرة، على سبيل المثال مشاركة قائمة الوالدين من الأطفال والسماح للأطفال .add و.remove أنفسهم؟ ولكن أنا لا أعرف الكثير حول هذا الموضوع.

وتحرير: حل آخر ممكن، وكان أول ما تبادر إلى الذهن، هو ببساطة إضافة sleep(1) في بداية /* child stuff */ إلا أن الروائح مضحك بالنسبة لي، وهذا هو السبب تركت بها. أنا أيضا لا بل متأكد من انها الإصلاح بنسبة 100٪.

وهكذا، كيف تصحيح هذا السباق شرط؟ وإذا كان هناك نمط أوصى راسخة لهذا، واسمحوا لي أن أعرف!

وشكرا.

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

المحلول

وأبسط حل سيكون لمنع إشارة SIGCHLD قبل fork() مع sigprocmask() وإلغاء حظره في التعليمات البرمجية الأم بعد أن كنت قد جهزت معرف المنتج.

إذا مات الطفل، معالج إشارة لSIGCHLD سوف يطلق بعد الافراج عن الإشارة. وهو مفهوم مقطع حرج - في قضيتك يبدأ مقطع حرج قبل fork() وينتهي بعد children.add()

نصائح أخرى

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

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

  // main program excerpt
    for (;;) {
      if ( is_time_to_make_babies ) {

        pid = fork();
        if (pid == -1) {
          /* fail */
        } else if (pid == 0) {
          /* child stuff */
          print "child started"
          exit
        } else {
          /* parent stuff */
          print "parent forked new child ", pid
          if (!earlyDeaths.contains(pid)) {
              children.add(pid);
          } else {
              earlyDeaths.remove(pid);
          }
        }

      }
    }

  // SIGCHLD handler
  sigchld_handler(signo) {
    while ( (pid = wait(status, WNOHANG)) > 0 ) {
      print "parent caught SIGCHLD from ", pid
      if (children.contains(pid)) {
          children.remove(pid);
      } else {
          earlyDeaths.add(pid);
      }
    }
  }

وتحرير: وهذا يمكن أن تكون مبسطة إذا عملية الخاص بك هو واحد مترابطة - earlyDeaths ليس من الضروري أن تكون وعاء، فإنه فقط أن عقد معرف المنتج واحد

وربما خوارزمية متفائلة؟ محاولة children.remove (PID)، وإذا فشلت، المضي قدما في الحياة.

وأو تحقق من معرف المنتج في الأطفال قبل محاولة إزالته؟

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