سؤال
يرجى النظر في 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)، وإذا فشلت، المضي قدما في الحياة.
وأو تحقق من معرف المنتج في الأطفال قبل محاولة إزالته؟