2016-01-16 2 views
3

Я учащийся. Чтобы лучше понять заботу и питание каналов и гортанов, я пытаюсь построить сито Эратосфена как набор гортинов, соединенных в трубопровод по каналам.Что такое элегантный способ закрыть цепочку каналов, связанных каналами?

Вот что я до сих пор:

// esieve implements a Sieve of Eratosthenes 
// as a series of channels connected together 
// by goroutines 
package main 

import "fmt" 

func sieve(mine int, inch chan int) { 
    start := true      // First-number switch 
    ouch := make(chan int)    // Output channel for this instance 
    fmt.Printf("%v\n", mine)    // Print this instance's prime 
    for next := <-inch; next > 0; next = <-inch { // Read input channel 
     fmt.Printf("%v <- %v\n",mine,next)   // (Trace) 
     if (next % mine) > 0 {      // Divisible by my prime? 
      if start {     // No; is it the first number through? 
       go sieve(next, ouch)  // First number - create instance for it 
       start = false   // First time done 
      } else {      // Not first time 
       ouch <- next    // Pass it to the next instance 
      } 
     } 
    } 
} 

func main() { 
    lim := 30      // Let's do up to 30 
    fmt.Printf("%v\n", 2)   // Treat 2 as a special case 
    ouch := make(chan int)  // Create the first segment of the pipe 
    go sieve(3, ouch)    // Create the instance for '3' 
    for prime := 3; prime < lim; prime += 2 { // Generate 3, 5, ... 
     fmt.Printf("Send %v\n", prime)  // Trace 
     ouch <- prime       // Send it down the pipe 
    } 
} 

И насколько она идет, она прекрасно работает.

Однако, когда я заканчиваю основной цикл, main выходит до того, как все номера, все еще находящиеся в конвейере sieve экземпляров, распространяются до конца.

Что является самым простым, самым элегантным или общепринятым способом сделать основную процедуру ожидания завершения набора goroutines (о котором он только «знает» первого)?

+0

Простым и рекомендуемым решением является использование другого канала, который 'main' будет блокировать и получать результат операции. – creker

+0

Возможный дубликат работы [Shutdown «рабочий» после процедуры, когда буфер пуст] (http://stackoverflow.com/questions/32383063/shutdown-worker-go-routine-after-buffer-is-empty) – icza

+0

@icza: I частично соглашаться; этот вопрос включает более простую (сортирующую) топологию. В моем случае только последний goroutine в трубе должен сигнализировать об отключении, * как только он знает, что он последний *. Я думаю, что я близок к окончательному ответу, который я в конечном итоге разместил здесь. BTW восхитительные конструкции для использования! –

ответ

2

Что касается вашего вопроса заголовка, убийство работника goroutines, когда вы не нуждаетесь в них больше: Вы можете использовать Done идиомы. Считывание с закрытого канала дает нулевое значение.

Создать новый канал done. Когда чтение с этого канала преуспевает, горуты знают, что они должны прекратить работу. Закройте канал в главном меню, когда у вас есть все необходимые значения.

Проверьте, можете ли вы читать по каналу done и выйти с помощью возврата или прочитать следующий, когда это доступно. Это частично заменяет присвоение next в вас циклю:

select { 
case <-done: 
return 
case next = <- inch: 
} 

пробегающий канал также работает, так как закрытие, что канал выходит из цикла.

Что касается обратного, вашего тела вопроса, ожидая набор goroutines закончить:

Использование sync.WaitGroup.

И когда каждый goroutine заканчивает:

wg.Done() 

Или используйте Defer:

defer wg.Done() 

Ждать для всех из них, чтобы сообщить, как это сделано:

wg.Wait() 

В ваш пример, просто позвоните wg.Add(1), когда вы запустите новый горутин, прежде чем позвонить wg.Done() и вернитесь. Пока вы только достигнете нуля один раз, wg.Wait() работает должным образом, поэтому wg.Add(1) до wg.Done.

0

После @izca разблокирован мой затора, и после нескольких неудачных попыток, связанных с тупики, когда все закончилось, вот мое решение работает правильно:

// esieve implements a Sieve of Eratosthenes 
// as a series of channels connected together 
// by goroutines 
package main 

import "fmt" 

func sieve(mine int,     // This instance's own prime 
      inch chan int,    // Input channel from lower primes 
      done chan int,    // Channel for signalling shutdown 
      count int) {    // Number of primes - counter 
    start := true      // First-number switch 
    ouch := make(chan int)   // Output channel, this instance 
    fmt.Printf("%v ", mine)   // Print this instance's prime 
    for next := <-inch; next > 0; next = <-inch { // Read input channel 
     if (next % mine) > 0 {  // Divisible by my prime? 
      if start {    // No; first time through? 
       go sieve(next, ouch, done, count+1) // First number, 
                // create instance for it 
       start = false   // First time done 
      } else {     // Not first time 
       ouch <- next   // Pass to next instance 
      } 
     } 
    } 
    if start {      // Just starting? 
     close(done)     // Yes - we're last in pipe - signal done 
     print("\n",count," primes\n") // Number of primes/goroutines 
    } else { 
     close(ouch)     // No - send the signal down the pipe 
    } 
} 

func main() { 
    lim := 100      // Let's do up to 100 
    done := make(chan int)   // Create the done return channel 
    ouch := make(chan int)   // Create the first segment of the pipe 
    go sieve(2, ouch, done, 1)  // Create the first instance for '2' 
    for prime := 3; prime < lim; prime += 1 { // Generate odd numbers 
     ouch <- prime       // Send numbers down the pipe 
    } 
    close(ouch)      // Send the done signal down the pipe 
    <- done       // and wait for it to come back 
} 

Я чрезвычайно впечатлен с элегантностью и простотой Go для этого вида программирования по сравнению со многими другими языками. Конечно, бородавки я требую для себя.

Если необходимо, я бы приветствовал критические замечания.

+0

В sieve() вы определяете локальную переменную «start», используя синтаксис «: =». Это означает, что каждое сито goproc, которое вы вызываете, будет думать, что это начальный процесс. – maurice

+0

@maurice Извините, имя, возможно, было плохо выбрано. Цель исходной переменной состоит в том, что она локальна для каждого goroutine и сообщает ей, завершил ли она запуск, прочитав первый штрих, который до сих пор доходит до этого канала до этого goroutine. У меня что-то не так? (Мне всегда было плохо при выборе имен переменных.) –

+0

ах, теперь я вижу, что вы разрешаете каждому сито goproc закрывать дочерний вход, только если он породил ребенка. Понял. Спасибо. – maurice

Смежные вопросы