goroutine and channel
goroutine
Multithreading
func hello() { //fmt.Printf("Hello Goroutine!!\n") for i:=0;i<100;i++ { fmt.Printf("hello:%d\n",i) time.Sleep(time.Millisecond) } } func main() { go hello() //A separate thread has been started, alternating with the following code to make it a multithread //fmt.Printf("main function\n") for i:=0;i<100;i++ { fmt.Printf("main:%d\n",i) time.Sleep(time.Millisecond) } time.Sleep(time.Second) //Fix the code so that the child thread can execute when the main thread exits }
Multiple goroutine s
func nunmers() { for i :=0;i<=5;i++ { time.Sleep(time.Millisecond*250) fmt.Printf("%d\n",i) } } func chars() { for i:='a';i<='e';i++ { time.Sleep(time.Millisecond*400) fmt.Printf("%c\n",i) } } func main() { go nunmers() go chars() time.Sleep(time.Second*3) }
Processes and threads
-
Process:
- A process is an execution process of a program in the operating system. The system is an independent unit for resource allocation and scheduling
-
Threading:
- Thread is an execution entity of a process and the basic unit of CPU scheduling and dispatching. It is smaller than a process and can run independently
- A process can create and undo multiple threads, and multiple threads in the same process can execute concurrently
Concurrent and parallel
- Multithreaded programs run on a core CPU, which is concurrent
- Multithreaded programs run on multiple cores of CPU, which is parallel
Coroutines and threads
- Collaboration: independent stack space, shared heap space, and scheduling controlled by users themselves. Essentially, it is similar to user level threads, which are also self implemented
- Thread: a county can run multiple threads, which are lightweight threads
goroutine's scheduling model
- M (thread) P (context) G (goroutine)
Set the number of CPU cores that golang runs
func main() { cpu := runtime.NumCPU() //Limit the number of cores (regardless of the new version), such as the monitoring program, which can control the resource consumption of its operation //runtime.GOMAXPROCS(1) for i:=0;i <=8;i++ { go func() { }() } fmt.Printf("%d\n",cpu) time.Sleep(time.Second*12) }
channel
-
How to communicate between different goroutine s:
- Global variables and lock synchronization: not recommended
- Channel: first in first out queue
- The concept of channel
- Like a pipe in unix
- FIFO
- Thread safety, multiple goroutine colleagues do not need to lock access
- There are types of channels. A certificate's channel can only be an integer
channel declaration
- Reference type, need to use make to initialize
var Variable name chan type var test chan int var test chan string var test chan map[string]string var test chan stu var test chan *stu
- channel initialization, reading and writing
func main() { //Var int channel channel int = make (channel int, 1) channel with buffer var intChan chan int = make(chan int) // Unbuffered channel fmt.Printf("intChanin:%p\n",intChan) go func() { //Write data intChan <- 100 fmt.Printf("insert item end\n") }() go func() { //Read data fmt.Printf("start\n") time.Sleep(time.Second*3) var a int a = <- intChan fmt.Printf("intChan:%d\n",a) }() time.Sleep(time.Second*5) }
Combination of goroutine and channel
- Producer consumer model
func senData(ch chan string) { ch <- "Washinton" ch <- "Tripoli" ch <- "LongDong" ch <- "Beijing" ch <- "Tokyo" } func getData(ch chan string) { var input string for { input = <- ch fmt.Printf("input=%s\n",input) } } func main() { ch := make(chan string,10) go senData(ch) go getData(ch) time.Sleep(time.Second*1) }
Blocking channel
-No write, empty read will block (empty channel) -If the number of writes exceeds the buffer, it will block (the channel is full)
func sendChans(ch chan string) { var i int for { var str string str = fmt.Sprintf("stu %d\n",i) fmt.Printf("write:%s\n",str) //If there is no buffer, the write is blocked ch <- str i++ } } func main() { ch := make(chan string,10) //Buffer zone //Ch: = make (Chan string) / / no buffer go sendChans(ch) time.Sleep(time.Second*200) }
Synchronization between channel s
- Add exit detection method to ensure synchronization
func sendChannels(ch chan string,exitCh chan bool) { ch <- "AA" ch <- "BB" ch <- "CC" ch <- "DD" ch <- "EE" close(ch) exitCh <- true } func getChannels(ch chan string,exitCh chan bool) { for { // Check channel off input,ok := <- ch if !ok { break } fmt.Printf("getchannels Medium input:%s\n",input) } exitCh <- true } func getChannels2(ch chan string,exitCh chan bool) { for { // Check channel off input,ok := <- ch if !ok { break } fmt.Printf("getchannels Medium input:%s\n",input) } exitCh <- true } func main() { ch := make(chan string) exitChan := make(chan bool,3) go sendChannels(ch,exitChan) go getChannels(ch,exitChan) go getChannels2(ch,exitChan) //Check the status three times, and then exit. Do not need time.sleep, wait for other goroutine to exit <- exitChan //Take out the elements and throw them away <- exitChan <- exitChan }
- Using waitGroup to ensure goroutine synchronization
func SendChannels1(ch chan string,waitGroup *sync.WaitGroup) { ch <- "AA" ch <- "BB" ch <- "CC" ch <- "DD" ch <- "EE" close(ch) fmt.Printf("send data exited\n") waitGroup.Done() //End goroutine's flag, and then set the number - 1 of the flag bit } func GetChannels1(ch chan string,waitGroup *sync.WaitGroup) { for { input,ok := <- ch if !ok{ break } fmt.Printf("getchannels Medium input:%s\n",input) } fmt.Printf("get data exited\n") waitGroup.Done() } func GetChannels2(ch chan string,waitGroup *sync.WaitGroup) { for { input,ok := <- ch if !ok{ break } fmt.Printf("getchannels Medium input:%s\n",input) } fmt.Printf("get data2 exited\n") waitGroup.Done() } func main() { var wg sync.WaitGroup ch := make(chan string) wg.Add(3) //Every time goroutine is executed, - 1 go SendChannels1(ch,&wg) go GetChannels1(ch,&wg) go GetChannels2(ch,&wg) wg.Wait() //Wait until the run is complete to return fmt.Printf("main goroutine exited\n") }
- When the channel is closed, the data of the producer channel is saved without loss
func main() { intChan := make(chan int,10) for i:=0;i<10;i++ { intChan<- i } //Close channel close(intChan) time.Sleep(time.Second) for j:=0;j<10;j++ { a := <- intChan fmt.Printf("a=%d\n",a) } }
- Traversal of channel s
func GetChannels2(ch chan string,waitGroup *sync.WaitGroup) { //for range will automatically determine whether the channel is closed for v :=range ch { fmt.Printf("get data2 %s\n",v) } fmt.Printf("get data2 exited\n") waitGroup.Done() }
- Closure of chan
- Use the built-in function close to close. After the chan is closed, the for range traverses the existing elements in the chan
- Use the built-in function close to close. After the chan is closed, there is no writing method for range. You need to use V, OK: = & lt; - chan to determine whether the chan is closed
Read only and write only of chan
It should be noted that & lt; - is in the position of chan keyword, and & lt; - is read-only on the left side of chan and write only on the right side
func chanPerms() { var readOnly <- chan int = make(chan int,100) // Readonly < - 100 read only and not writable var writeOnly chan <- int = make(chan int,10) // < - writeonly write only unreadable }
- Usage scenario: three parties call read-only write only permission control to prevent misoperation
select chan
func main() { var intChan chan int = make(chan int,10) var strChan chan string = make(chan string,10) var wg sync.WaitGroup wg.Add(2) // insert data go func() { var count int for count < 1000 { count++ select { case intChan <- 10: fmt.Printf("write to int chan succ\n") case strChan <- "hello": fmt.Printf("write to str chan succ\n") default: fmt.Printf("all chan is full\n") time.Sleep(time.Second) } } wg.Done() }() wg.Wait() //Read data go func() { var count int for count < 10000 { count ++ select { case a := <- intChan: fmt.Printf("read from int chain a:%d\n",a) case <- strChan: fmt.Printf("read from str chan\n") default: fmt.Printf("all chan is empty\n") time.Sleep(time.Second) } } wg.Done() }() wg.Wait() }