1. Concurrent using sync:
package main import ( "fmt" "sync" ) func main() { testSlice := []string{"test1", "test2", "test3"} wg := sync.WaitGroup{} for _, t := range testSlice { wg.Add(1) go printSlice(t, &wg) } wg.Wait() } func printSlice(s string, wg *sync.WaitGroup) { defer wg.Done() fmt.Printf("this is %+v\n", s) }
2. Using pipelines for concurrency
Pipeline is the data type originally supported by go, and it can also achieve concurrent effect.
The general idea is that when the data in the pipeline is retrieved from the main consortium, the pipeline will be blocked, and the data will be stuffed into the pipeline during the transmission consortium, and the pipeline will be closed after the data is blocked; when the data can not be retrieved from the main consortium, the pipeline will be closed, and the task will be completed.
package main import "fmt" var channel = make(chan int, 10) func main() { go func() { for i := 0; i < 10; i++ { channel <- i } close(channel) //After putting the data, we must close chan, otherwise it will be deadlocked. }() for v := range channel { fmt.Println(v) } //The main routine will not quit because it will be blocked all the time if it cannot be retrieved from chan in the main routine. //If another one is blocked in another one, but the main one is not blocked, and the data acquisition process is not yet reached, the main one will quit. }
Advanced Edition
package main import ( "fmt" ) var channel = make(chan int) var result = make(chan int) func main() { go func() { for i := 0; i < 100; i++ { channel <- i } close(channel) }() ct := 0 for c := range channel { go func(i int) { result <- i + i }(c) } for v := range result { ct++ fmt.Println(v) if ct == 100 { break } } }
Note that closing the pipeline and monitoring the value of the pipeline need to be two different processes. If both operations are in one process, either a closed protocol or an unclosed protocol will be monitored, an exception will occur.
Here, the capacity of the pipeline is 0, that is, each time the data into the pipeline plug needs to be consumed before it can continue to plug; the capacity of the protocol can be plugged into the number of capacity data, and then the data needs to be blocked until there is space;
3. select keyword
Look at the code first.
start := time.Now() c := make(chan interface{}) ch1 := make(chan int) ch2 := make(chan int) go func() { time.Sleep(4*time.Second) close(c) }() go func() { time.Sleep(3*time.Second) ch1 <- 3 }() go func() { time.Sleep(3*time.Second) ch2 <- 5 }() fmt.Println("Blocking on read...") select { case <- c: fmt.Printf("Unblocked %v later.\n", time.Since(start)) case <- ch1: fmt.Printf("ch1 case...") case <- ch2: fmt.Printf("ch2 case...") default: fmt.Printf("default go...") }
If default, ch1 and CH2 branches are commented out, a random execution will be selected. If the sleep time of ch1 and CH2 is changed to 10 seconds, the c branch will be executed.
Namely
- If one or more pipeline operations can be completed, the Go runtime system will randomly select an execution. Otherwise, if there is a default branch, the default branch statement will be executed. If there is no default, the select statement will be blocked until at least one pipeline operation can be performed.
- There needs to be a real goroutine. If all the subgoroutines for pipeline operations have exited, select {} will report to panic.
Some references: https://www.jianshu.com/p/2a1...