By default make(chan int) creates an unbuffered channel. Which means that every receive-operation <-intChannel is blocking until a value is received.
Therefore each of <-intChannel's will be unblocked once a value is received, thus we can unblock all receivers by enough number of send operations intChannel <- 0.
4. Concurrency Limit by Buffered Channel and Await Coroutines by sync.WaitGroup
4.1.
Download a set of large files
4.1.1.
Structs for Decoding an XML
We define the following struct in an attemp to decode the XML file from this link:
Here we define a wait group wg, we will add a counter by wg.Add(1)right before we dispatch a coroutine.
We intentionally create a semaphore with empty struct struct{}, which therefore pre-allocates no memory to our channel since this is not a meaningful data type.
Here we wg.Done() in coroutine to deduct the counter
We wg.Wait() to block the main thread from running
We use a pair of semaphore <- struct{}{} and <-semaphore to rate limit the operations. The send action semaphore <- is blocking when the capacity of the channel is full, resulting in a rate limit.
Note that here we cannot simply define the closure go func(){ ... }() with item being captured from the parent scope. It is because the order of execution of the closures is not determined by the sequential order we define it.
If they refer to the same variable, then a race condition occurs since the value of the reference item (an auto-derefereneced pointer) is ever changing.
We wrap up by counting the duration of execution:
39 elapsed := time.Since(start)40 fmt.Printf("This code took %s to run.", elapsed)41}
5. Atomic Operations by Mutex Lock
We simply assign the target variable a lock by wrapping it into a new struct with mutex: