我可以得到一些帮助来解释“并发主筛”示例吗?

I am very new to go, Can someone help me to reason about this example:

// A concurrent prime sieve

package main

// Send the sequence 2, 3, 4, ... to channel 'ch'.
func Generate(ch chan<- int) {
    for i := 2; ; i++ {
        ch <- i // Send 'i' to channel 'ch'.
    }
}

// Copy the values from channel 'in' to channel 'out',
// removing those divisible by 'prime'.
func Filter(in <-chan int, out chan<- int, prime int) {
    for {
        i := <-in // Receive value from 'in'.
        println("debug", i, prime)
        if i%prime != 0 {
            out <- i // Send 'i' to 'out'.
        }
    }
}

// The prime sieve: Daisy-chain Filter processes.
func main() {
    ch := make(chan int) // Create a new channel.
    go Generate(ch)      // Launch Generate goroutine.
    for i := 0; i < 10; i++ {
        prime := <-ch
        print(prime, "
")
        ch1 := make(chan int)
        go Filter(ch, ch1, prime)
        ch = ch1
    }
}

(Go Playground)

There were 2 points I am still very confused, it would be much appreciated if some one can give me some insight about the code.

  1. ch = ch1 it looks elegant, the result is defnitely inaccurate without this line, but I don’t know the details why need to keep updating the input channel with output channel.

  2. I also added some debug information. I am very surprised all non prime number is filtered out very efficiently. i.e 10 (not prime) is just checked once. There was no debug 10 3 after debug 10 2. I suspect it is if i%prime != 0 do the trick. but how is consistently working with number 9.

Debug output:

debug 9 2
debug 9 3
debug 10 2
debug 11 2
debug 11 3
  1. This is why it is called a prime sieve. Each channel connects one sieve/Filter to the next one (coarser sieve). That is why you connect input to output sieves:

    sieve out multiples of 2 ---> sieve out multiples of 3 ---> sieve out multiples of 5 ---> sieve out ....

    You see: What flows out of one sieve goes to the next sieve/Filter.

  2. I do not understand the question. 9 is not devisable by 2 so it passes the 2-Filter. 9 is devisable by 3 so it is stopped by the 3-Filter.

  • in a simple way imagine every channel is a set of numbers (first channel which gets fed by generate func contains all integer numbers) with a leading number which is the first one (we get it by prime := <-ch). then it checks if other numbers of the set (numbers sent to channel) is prime from the leadeing number, and put primes to the next set. notice it keeps going in the background (and you will run out of memory space ofc) and proceed to the next level which is using the next number set (or channel) and compare its numbers with leading number and so on. so the first element of every channel is prime (mathematical logic) and other numbers will survive (get to next channel) only if its prime to first element
  • the reason 10 is checked once is it gets out of system when we check it compare it with 2 cuase its not prime to 2, but you can see that numbers like 19 which is prime to every number below it get checked by every lower prime number till 17