W
W
wargych2018-04-18 05:29:11
go
wargych, 2018-04-18 05:29:11

Correct use of channels (effective go)?

I read Effective Go, channel section. Here's what I don't understand, there is code:

var sem = make(chan int, MaxOutstanding)

func handle(r *Request) {
    sem <- 1    // Wait for active queue to drain.
    process(r)  // May take a long time.
    <-sem       // Done; enable next request to run.
}

func Serve(queue chan *Request) {
    for {
        req := <-queue
        go handle(req)  // Don't wait for handle to finish.
    }
}

Further text:
This design has problems: Serve creates a new goroutine for each incoming request, and no more than MaxOutstanding will be running at one time. If the number of requests increases too quickly, then as a result, the program may require an infinite amount of resources.
.
Question 1 - what can an infinite amount of resources spend on if we can regulate the number of goroutines through the MaxOutstanding parameter - on a queue of requests to a blocked channel?
Further:
We can solve this by changing Serve using changes in the number of spawned goroutines. Here's the obvious solution, but be careful as it has a bug that we'll fix later:

func Serve(queue chan *Request) {
    for req := range queue {
        sem <- 1
        go func() {
            process(req) // Buggy; see explanation below.
            <-sem
        }()
    }
}

Question 2 - what is the fundamental difference? In the first case, we received a request to the request channel, sent the request to the handler, and, using the buffered channel as a semaphore to limit the number of goroutines by MaxOutstanding, launched a certain process, passing the request to it.
In the second case - already without a handler, we go through the request channel, assign the request to req, open the semaphore, start the goroutine with the execution of the process with the transfer of the request there, close the semaphore from the goroutine. I don't see what should be the difference in the result?
Question 3 - Appd. - I already understood, the question was asked out of inattention.
Move on:
The bug is that in the Go language the for loop, the variable loop is reused for each iteration, so the req variables are shared across all goroutines. This is not what we want. We need to make sure req is unique for each goroutine. Here is one way to pass the value of req as an argument to close the goroutine:

func Serve(queue chan *Request) {
    for req := range queue {
        sem <- 1
        go func(req *Request) {
            process(req)
            <-sem
        }(req)
    }
}

Question 4 - again, what's the difference in the result?
What kind of game is in the text with the division of req across all goroutines? In my understanding, we received req during iteration - called a goroutine, new req - a new goroutine, what's the problem?
In general, there are a lot of questions, please clarify something, how :))

Answer the question

In order to leave comments, you need to log in

1 answer(s)
P
Papa, 2018-04-18
@wargych

All answers to your questions are here: https://www.ozon.ru/context/detail/id/34671680/

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question