मैंने प्रश्न को कुछ बार पढ़ा है, और मुझे लगता है कि मैं आपको क्या करने की कोशिश कर रहा हूं। आपके पास एक नियंत्रण स्क्रिप्ट है। यह स्क्रिप्ट बच्चों को कुछ सामान करने के लिए स्पॉन्स करती है, और इन बच्चों ने वास्तव में काम करने के लिए पोते को जन्म दिया। समस्या यह है कि पोते बहुत धीमे (एसटीडीआईएन, या जो कुछ भी इंतजार कर रहे हैं) हो सकते हैं, और आप उन्हें मारना चाहते हैं। इसके अलावा, यदि एक धीमी पोती है, तो आप पूरे बच्चे को मरना चाहते हैं (अन्य पोते-पोतों को मारना, यदि संभव हो)।
तो, मैंने इन दो तरीकों को लागू करने का प्रयास किया। सबसे पहले माता-पिता को एक नए यूनिक्स सत्र में बच्चे को जन्म देने के लिए बनाया गया था, कुछ सेकंड के लिए टाइमर सेट करें, और टाइमर बंद होने पर पूरे बच्चे सत्र को मार दें। इसने माता-पिता को बच्चे और पोते दोनों के लिए ज़िम्मेदार बनाया। यह भी सही काम नहीं किया।
अगली रणनीति माता-पिता को बच्चे को जन्म देने के लिए थी, और फिर बच्चे को पोते के प्रबंधन के लिए जिम्मेदार बनाते हैं। यह प्रत्येक पोते के लिए एक टाइमर सेट करेगा, और अगर प्रक्रिया समाप्ति समय से बाहर नहीं है तो इसे मार दें। यह बहुत अच्छा काम करता है, तो यहां कोड है।
हम बच्चों और टाइमर का प्रबंधन करने के लिए ईवी का उपयोग करेंगे, और एपीआई के लिए AnyEvent। (आप इवेंट या पीओई जैसे किसी भी एनीवेंट इवेंट लूप को आजमा सकते हैं। लेकिन मुझे पता है कि ईवी सही ढंग से उस स्थिति को संभालती है जहां एक बच्चे से बाहर निकलता है इससे पहले कि आप इसे मॉनिटर करने के लिए लूप बताएं, जो कष्टप्रद दौड़ शर्तों को समाप्त करता है जो अन्य लूप कमजोर हैं ।
# active child watchers
my %children;
फिर हम एक समारोह बच्चों शुरू करने के लिए लिखने के लिए की जरूरत है:)
#!/usr/bin/env perl
use strict;
use warnings;
use feature ':5.10';
use AnyEvent;
use EV; # you need EV for the best child-handling abilities
हम बच्चे पर नजर रखने वालों का ट्रैक रखने की जरूरत है। चीजें माता-पिता के स्पंज बच्चों को बुलाते हैं, और बच्चों को स्पॉन को नौकरियां कहा जाता है।
sub start_child([email protected]) {
my ($on_success, $on_error, @jobs) = @_;
तर्क coderef कार्यों की सूची एक कॉलबैक जब बच्चे पूरा करता सफलतापूर्वक (जिसका अर्थ है अपनी नौकरियों को भी एक सफलता थे) के नाम से जाना, एक कॉलबैक जब बच्चे सफलतापूर्वक पूर्ण नहीं हुआ है, और फिर रहे हैं चलाने के लिए।
इस फ़ंक्शन में, हमें कांटा जाना होगा। माता-पिता के लिए, हम सेटअप एक बच्चे द्रष्टा बच्चे की निगरानी के लिए:
if(my $pid = fork){ # parent
# monitor the child process, inform our callback of error or success
say "$$: Starting child process $pid";
$children{$pid} = AnyEvent->child(pid => $pid, cb => sub {
my ($pid, $status) = @_;
delete $children{$pid};
say "$$: Child $pid exited with status $status";
if($status == 0){
$on_success->($pid);
}
else {
$on_error->($pid);
}
});
}
बच्चे में, हम वास्तव में नौकरियों चलाते हैं। इसमें सेटअप का थोड़ा सा हिस्सा शामिल है, हालांकि।
सबसे पहले, हम माता-पिता के बच्चे निरीक्षक को भूल जाते हैं, क्योंकि यह बच्चे को अपने भाई बहनों से बाहर निकलने के लिए समझ में नहीं आता है। (कांटा मज़ा है, क्योंकि आप माता-पिता के राज्य के सभी वारिस, तब भी जब कि बिल्कुल कोई मतलब नहीं है है।)
else { # child
# kill the inherited child watchers
%children =();
my %timers;
हम यह भी पता करने के लिए जब सभी नौकरियों किया जाता है की जरूरत है, और चाहे या नहीं वे सभी एक सफलता थीं। हम गिनती सशर्त चर का उपयोग पर करते हैं जब सबकुछ निकलता है। हम स्टार्टअप पर वृद्धि करते हैं, और बाहर निकलने पर कमी, और जब गिनती 0 होती है, तो हम सबकुछ जानते हैं।
मैं त्रुटि स्थिति को इंगित करने के लिए चारों ओर एक बुलियन भी रखता हूं। एक प्रक्रिया एक गैर शून्य की स्थिति के साथ बाहर निकल जाता है, त्रुटि अन्यथा 1. करने के लिए चला जाता है, यह रहता है 0. आप अधिक राज्य रखने के लिए चाहते हो सकता है की तुलना में इस :)
# then start the kids
my $done = AnyEvent->condvar;
my $error = 0;
$done->begin;
(हम भी 1 पर गिनती शुरू ताकि अगर 0 नौकरियां हों, तो हमारी प्रक्रिया अभी भी बाहर निकलती है।)
अब हमें प्रत्येक नौकरी के लिए कांटा और नौकरी चलाने की आवश्यकता है। अभिभावक में, हम कुछ चीजें करते हैं। हम condvar में वृद्धि। हम बच्चे को बहुत धीमा होने पर मारने के लिए टाइमर सेट करते हैं। और हम एक बच्चे के दर्शक को स्थापित करते हैं, इसलिए हम नौकरी की निकास स्थिति के बारे में सूचित हो सकते हैं। क्योंकि यह इसके साथ राज्य वहन करती है
for my $job (@jobs) {
if(my $pid = fork){
say "[c] $$: starting job $job in $pid";
$done->begin;
# this is the timer that will kill the slow children
$timers{$pid} = AnyEvent->timer(after => 3, interval => 0, cb => sub {
delete $timers{$pid};
say "[c] $$: Killing $pid: too slow";
kill 9, $pid;
});
# this monitors the children and cancels the timer if
# it exits soon enough
$children{$pid} = AnyEvent->child(pid => $pid, cb => sub {
my ($pid, $status) = @_;
delete $timers{$pid};
delete $children{$pid};
say "[c] [j] $$: job $pid exited with status $status";
$error ||= ($status != 0);
$done->end;
});
}
टाइमर का उपयोग करना, एक छोटा सा अलार्म की तुलना में आसान है। प्रत्येक टाइमर जानता है कि किस प्रक्रिया को मारना है, और यह प्रक्रिया आसान होने पर टाइमर को रद्द करने के लिए आसान है - हम केवल हैश से इसे हटा दें।
यह माता-पिता (बच्चे का) है। (बच्चे की, या काम) बच्चे वास्तव में सरल है:
else {
# run kid
$job->();
exit 0; # just in case
}
तुम भी पास stdin यहाँ, अगर आप चाहते थे सकता है।
अब, सभी प्रक्रियाओं के बाद उत्पन्न होने के बाद, हम उन्हें कन्वेयर पर प्रतीक्षा करके सभी बाहर निकलने का इंतजार करते हैं।घटना पाश बच्चों और टाइमर monior जाएगा, और हमारे लिए सही काम: की तरह,
} # this is the end of the for @jobs loop
$done->end;
# block until all children have exited
$done->recv;
तब, जब सभी बच्चों को बाहर आ गए हैं, हम जो कुछ भी सफाई काम हम चाहते हैं कर सकते हैं:
if($error){
say "[c] $$: One of your children died.";
exit 1;
}
else {
say "[c] $$: All jobs completed successfully.";
exit 0;
}
} # end of "else { # child"
} # end of start_child
ठीक है, तो यह बच्चा और पोती/नौकरी है। अब हमें माता-पिता को लिखना होगा, जो कि बहुत आसान है।
बच्चे की तरह, हम बच्चों के लिए प्रतीक्षा करने के लिए एक गिनती कोंवर का उपयोग करने जा रहे हैं।
# main program
my $all_done = AnyEvent->condvar;
हमें कुछ नौकरियों की आवश्यकता है। यहाँ एक हमेशा सफल नहीं है, और एक है कि सफल हो जाएगा अगर आप वापसी प्रेस है, लेकिन अगर आप बस इसे टाइमर द्वारा मारा जा जाने असफल हो जायेगी:
my $good_grandchild = sub {
exit 0;
};
my $bad_grandchild = sub {
my $line = <STDIN>;
exit 0;
};
तो फिर हम सिर्फ बच्चे शुरू करने के लिए की जरूरत है नौकरियों। यदि आपको start_child
के शीर्ष पर वापस याद है, तो इसमें दो कॉलबैक, त्रुटि कॉलबैक और सफलता कॉलबैक शामिल है। हम उन्हें स्थापित करेंगे; त्रुटि कॉलबैक "ठीक नहीं" प्रिंट करेगा और कन्वेयर को कम करेगा, और सफलता कॉलबैक "ठीक" प्रिंट करेगा और वही करेगा। बहुत आसान।
my $ok = sub { $all_done->end; say "$$: $_[0] ok" };
my $nok = sub { $all_done->end; say "$$: $_[0] not ok" };
तो हम और भी अधिक पोते नौकरियों के साथ बच्चों का एक समूह शुरू कर सकते हैं:
say "starting...";
$all_done->begin for 1..4;
start_child $ok, $nok, ($good_grandchild, $good_grandchild, $good_grandchild);
start_child $ok, $nok, ($good_grandchild, $good_grandchild, $bad_grandchild);
start_child $ok, $nok, ($bad_grandchild, $bad_grandchild, $bad_grandchild);
start_child $ok, $nok, ($good_grandchild, $good_grandchild, $good_grandchild, $good_grandchild);
उन लोगों में से दो का समय-समाप्त होगा, और दो सफल होगा। यदि आप चल रहे हैं, तो दर्ज करें, फिर भी, वे सभी सफल हो सकते हैं।
वैसे भी, एक बार उन शुरू कर दिया है, हम बस समाप्त करने के लिए उनके लिए प्रतीक्षा करने की आवश्यकता:
$all_done->recv;
say "...done";
exit 0;
और वह कार्यक्रम है।
एक बात है कि हम नहीं कर रहे हैं कि समानांतर :: ForkManager करता "दर सीमित" हमारे कांटे ताकि केवल n
बच्चों को एक समय में चल रहे हैं। यह मैन्युअल रूप से लागू करने के लिए बहुत आसान है, हालांकि यह है:
use Coro;
use AnyEvent::Subprocess; # better abstraction than manually
# forking and making watchers
use Coro::Semaphore;
my $job = AnyEvent::Subprocess->new(
on_completion => sub {}, # replace later
code => sub { the child process };
)
my $rate_limit = Coro::Semaphore->new(3); # 3 procs at a time
my @coros = map { async {
my $guard = $rate_limit->guard;
$job->clone(on_completion => Coro::rouse_cb)->run($_);
Coro::rouse_wait;
}} ({ args => 'for first job' }, { args => 'for second job' }, ...);
# this waits for all jobs to complete
my @results = map { $_->join } @coros;
लाभ यहां आप अन्य बातों के, जबकि अपने बच्चों चल रहे हैं क्या कर सकते हैं वह यह है कि - बस async
के साथ और अधिक धागे अंडे इससे पहले कि आप करना शामिल होने के अवरुद्ध। एनीएवेंट :: सबप्रोसेस के साथ बच्चों पर आपके पास बहुत अधिक नियंत्रण है - आप बच्चे को पीटीआई में चला सकते हैं और फ़ीड कर सकते हैं, यह stdin (अपेक्षा के साथ), और आप अपने stdin और stdout और stderr को कैप्चर कर सकते हैं, या आप उन चीजों को अनदेखा कर सकते हैं, या जो भी हो। आप पर निर्णय लेते हैं, कुछ मॉड्यूल लेखक नहीं जो चीजों को "सरल" बनाने की कोशिश कर रहे हैं।
वैसे भी, उम्मीद है कि इससे मदद मिलती है।
वहाँ एक कारण है कि आप बच्चों में STDIN बंद नहीं करते है? – jrockway
यह भी एक अच्छा विचार हो सकता है। मुझे इसके बारे में सोचना होगा, हालांकि मैं कुछ बुरे मामलों को संभालने पर विचार कर रहा था जैसे उन्हें ऐसा करने की उम्मीद है कि उन्हें ऐसा करने की ज़रूरत है। –
नीचे लंबा जवाब देखें। लेकिन मूल रूप से, मुझे लगता है कि आपको समानांतर की तुलना में स्थिति पर अधिक नियंत्रण की आवश्यकता है :: फोर्कमैनेजर आपको देता है, जिसका अर्थ है कि आपको अपना खुद का रोल करना होगा। – jrockway