Вы должны написать это сами.
Программа запускается, пока не вернется. Каждая программа сама решает, когда возвращаться.
Предположим, у вас есть функции / процедуры A, B и C, каждая из которых выполняет длинные вычисления, и один из трех может найти полезный ответ первым, а два другихдолжен остановиться, если так. В этом случае вы захотите выделить три программы, которые выполняют вычисления:
func doA(args) {
... do computing for A ...
... deliver a result ...
return // this line is redundant, and here only for illustration
}
func doB(args) {
... do computing for B ...
... deliver a result ...
}
и т. Д.
Что входит в args
? Ну, это зависит от вас, но это хорошая идея дать всем трем функциям какой-то способ узнать, что одна из других дала полезный ответ, и они должны остановиться. Довольно умный способ сделать это состоит в том, чтобы иметь канал, который кто-то - кто бы ни решил, что результат «полезен», например - закрывает , чтобы указать, что все остальные должны прекратить работать. Все функции do
могут выглядеть следующим образом:
func doA(done chan struct{}, other_args) {
var result_ready bool
for !result_ready {
select {
case <-done: // someone else delivered a good result
return // so stop working now
default:
... work a bit more ...
}
}
... deliver result ...
}
Это все еще оставляет часть «результата доставки». Куда идет результат? Это тоже зависит от вас, но хороший способ справиться с этим состоит в том, чтобы иметь канал для каждой подпрограммы, который может дать результат, если он есть.
Здесь есть несколько хитростей, которые следует рассмотреть. Предположим, что драйвер main
хочет просмотреть несколько результатов и выбрать «достаточно хороший» по некоторым показателям, которые мы не хотим кодировать в каждом работнике. Каждый работник должен сделать некоторую работу и отправить пока ответ, а затем продолжать работать , пока main
не скажет "Мне нравится один из полученных ответов", main
закрыв канал done
,Затем мы получаем структуру кода, подобную этой:
func doA(done chan struct{}, resultChan chan resulttype, args) {
for {
select {
case <-done:
return
default:
... do a little work ...
... try to deliver result-so-far ...
}
}
}
Попытка получить результат до сих пор должна выглядеть так:
select {
case <-done:
return
case resultChan <- result:
}
Thisвполне может устранить необходимость в более раннем select
: у нас есть вещь, которая вычисляет A
вычисление, пока не будет готов результат, затем обрабатывают либо «вы можете выйти сейчас», либо «ваш результатотправлено ", в зависимости от того, что произойдет первым. Если сначала вы можете выйти, вы можете выйти. Если «ваш результат отправляется», он сразу же приступает к решению проблемы next .
В худшем случае doA
работает немного дольше, чем нужно: сколько бы он ни длилсятребуется, чтобы получить один результат.
Обратите внимание, что концепция "готового канала", наряду с множеством других лесов, полезных во многих реальных программах, содержится в идее context . См. Шаблоны Go Concurrency: контекст и, более прямо относящиеся к вашей проблеме, Шаблоны Go Concurrency: конвейеры и аннулирование .