The go keyword can be used to open a goroutine for task processing, and channel s are needed if communication is required between multiple tasks.
func testSimple(){ intChan := make(chan int) go func() { intChan <- 1 }() value := <- intChan fmt.Println("value : ", value) }
A simple example of this is that a newly opened goroutine sends a value of 1 to intChan, and intChan on the main thread receives this value.
channel type: Unbuffered and buffered
Channel has two forms, one is buffered, and once a thread sends a message to the channel, it blocks the current thread until it knows that other threads are going to receive the message.The form of no buffer is as follows:
intChan := make(chan int)
A buffered channel is a channel that can specify the number of buffered messages. When the number of messages is less than the specified value, no blocking will occur, and then no blocking will occur until the number exceeds. It needs to wait for other threads to receive channel processing. The form of buffering is as follows:
//3 is the number of buffers
intChan := make(chan int, 3)
Transfer struct structure data
Channels can transfer basic types of data, such as int, string, and also struct data
type Person struct { Name string Age uint8 Address Addr } type Addr struct { city string district string } /* Test channel to transfer complex Struct data */ func testTranslateStruct() { personChan := make(chan Person, 1) person := Person{"xiaoming", 10, Addr{"shenzhen", "longgang"}} personChan <- person person.Address = Addr{"guangzhou", "huadu"} fmt.Printf("src person : %+v \n", person) newPerson := <-personChan fmt.Printf("new person : %+v \n", newPerson) }
Here you can see that Custom Person objects can be transferred through channels, while one end modifies the data without affecting the other end, that is, the data transferred through channels is independent.
Turn off channel
Channels can be closed, for example, if a section of writing closes the channel, then one end of the reading side can detect a read failure when reading
/* Test Shutdown channel */ func testClose() { ch := make(chan int, 5) sign := make(chan int, 2) go func() { for i := 1; i <= 5; i++ { ch <- i time.Sleep(time.Second) } close(ch) fmt.Println("the channel is closed") sign <- 0 }() go func() { for { i, ok := <-ch fmt.Printf("%d, %v \n", i, ok) if !ok { break } time.Sleep(time.Second * 2) } sign <- 1 }() <-sign <-sign }
Combining outputs from multiple channel s
Data from multiple channels can be combined into one channel for output to form a message queue
/** Merge multiple input channels into one channel */ func testMergeInput() { input1 := make(chan int) input2 := make(chan int) output := make(chan int) go func(in1, in2 <-chan int, out chan<- int) { for { select { case v := <-in1: out <- v case v := <-in2: out <- v } } }(input1, input2, output) go func() { for i := 0; i < 10; i++ { input1 <- i time.Sleep(time.Millisecond * 100) } }() go func() { for i := 20; i < 30; i++ { input2 <- i time.Sleep(time.Millisecond * 100) } }() go func() { for { select { case value := <-output: fmt.Println("Output:", value) } } }() time.Sleep(time.Second * 5) fmt.Println("Main Thread Exit") }
Notification of exit through channel
Defines a channel, such as quit, for quit to exit. A thread that constantly executes tasks listens for quit reads through select. When a message in quit is read, it exits the current task thread, where the main thread notifies the task thread to exit.
/* Test channel for notifying interrupt exit issues */ func testQuit() { g := make(chan int) quit := make(chan bool) go func() { for { select { case v := <-g: fmt.Println(v) case <-quit: fmt.Println("B Sign out") return } } }() for i := 0; i < 3; i++ { g <- i } quit <- true fmt.Println("testAB Sign out") }
Producer-consumer issues
A producer-consumer model can be easily implemented through a channel, where a producer thread, a consumer thread, and a producer thread send messages to the channel while blocking, and the consumer thread polls for messages in the channel.
Processing and then blocking, the producer thread wakes up and continues with the logic behind it, thus forming a simple producer-consumer model.At the same time, once the producer has finished sending all the messages, the consumer thread can be notified to quit via quit.
When the consumer thread exits, the main thread is notified to exit and the whole program finishes exiting.
/** Producer-consumer issues */ func testPCB() { fmt.Println("test PCB") intchan := make(chan int) quitChan := make(chan bool) quitChan2 := make(chan bool) value := 0 go func() { for i := 0; i < 3; i++ { value = value + 1 intchan <- value fmt.Println("write finish, value ", value) time.Sleep(time.Second) } quitChan <- true }() go func() { for { select { case v := <-intchan: fmt.Println("read finish, value ", v) case <-quitChan: quitChan2 <- true return } } }() <-quitChan2 fmt.Println("task is done ") }
Output Order Problem
/* The output of this result is 1,2, or 2,1, or 2, and the order is variable */ func testSequnse() { ch := make(chan int) go func() { v := <-ch fmt.Println(v) }() ch <- 1 fmt.Println("2") }
What is the result of this output above?If you run it, you will find that it may be 1,2, 2,1, or 2. The order is not certain, so why is this? I think it's two different threads.
They run independently, and when v: = <-ch executes, both the main thread and the current thread are running (non-blocking). Whether the main thread executes first or the output of the new thread depends on whether the cpu is running, so the result is uncertain.
channel timeout
Channel timeout is achieved through time, and when a channel reads for more than a certain period of time without a message arriving, it will be notified of the timeout process to prevent the current thread from being blocked all the time
/* Check channel read-write timeouts and handle them */ func testTimeout() { g := make(chan int) quit := make(chan bool) go func() { for { select { case v := <-g: fmt.Println(v) case <-time.After(time.Second * time.Duration(3)): quit <- true fmt.Println("Timeout, notify the main thread to exit") return } } }() for i := 0; i < 3; i++ { g <- i } <-quit fmt.Println("Exit notification, main thread exits") }
channel input-output type specification
channel can specify whether it is input or output type in the display, and cannot use it to output messages. Otherwise, error compilation fails. Similarly, output type cannot accept message input.
This prevents handwriting errors in input and output types from causing program errors when writing code.Specifying the input-output type can be set at the time of the method parameter, so it only restricts the input-output in the current method.
See the implementation below.
/* Specifies whether the channel is input or output, preventing errors from being written at compile time. If specified, errors can be checked at compile time */ func testInAndOutChan() { ch := make(chan int) quit := make(chan bool) //Input type chan In this format: inChan chan<- int,If converted to output type, errors will occur during compilation go func(inChan chan<- int) { for i := 0; i < 10; i++ { inChan <- i time.Sleep(time.Millisecond * 500) } quit <- true quit <- true }(ch) go func(outChan <-chan int) { for { select { case v := <-outChan: fmt.Println("print out value : ", v) case <-quit: fmt.Println("Exit notification received") return } } }(ch) <-quit fmt.Println("Exit notification, main thread exits") }
channel implements concurrency control
Maximum concurrency is achieved by setting a channel with a buffer number, which is the number of buffers. The task starts with a limit that sends messages.
Read the message from this limit after the task is executed, so that when the number of concurrencies reaches the limit's buffer, limit <- true will block and stop here
Create a new thread that reads data from a limit until a thread has finished executing a task, thereby guaranteeing that the maximum number of concurrencies is controlled in the number of buffers.
/* The test handles events by channel ing the maximum number of concurrencies */ func testMaxNumControl() { maxNum := 3 limit := make(chan bool, maxNum) quit := make(chan bool) for i:=0; i<100; i++{ fmt.Println("start worker : ", i) limit <- true go func(i int) { fmt.Println("do worker start: ", i) time.Sleep(time.Millisecond * 20) fmt.Println("do worker finish: ", i) <- limit if i == 99{ fmt.Println("Complete Task") quit <- true } }(i) } <-quit fmt.Println("Upon receipt of exit notification, the main program exits") }
channel listening for interrupt signals
You can create a channel for a signal signal signal and listen for the os.Interrupt interrupt through signal.Notify at the same time, so it will be blocked here when <-quit is executed.
The main program will exit when you receive the os.Interrupt interrupt signal, such as pressing Ctrl+C to interrupt the program.Of course, you can also listen to other signals, such as os.Kill.
/* channel listening for interrupt signals */ func testSignal() { quit := make(chan os.Signal) signal.Notify(quit, os.Interrupt) go func() { time.Sleep(time.Second * 2) number := 0; for{ number++ println("number : ", number) time.Sleep(time.Second) } }() fmt.Println("Press Ctrl+C Exit program") <- quit fmt.Println("Main program exits") }
channel implements synchronous control, producer-consumer model
Open multiple threads to make and spend money, share the remaining amount variable, remainMoney, to implement the producer-consumer model
//Synchronization control model, producer model var lockChan = make(chan int, 1) var remainMoney = 1000 func testSynchronize() { quit := make(chan bool, 2) go func() { for i:=0; i<10; i++{ money := (rand.Intn(12) + 1) * 100 go testSynchronize_expense(money) time.Sleep(time.Millisecond * time.Duration(rand.Intn(500))) } quit <- true }() go func() { for i:=0; i<10; i++{ money := (rand.Intn(12) + 1) * 100 go testSynchronize_gain(money) time.Sleep(time.Millisecond * time.Duration(rand.Intn(500))) } quit <- true }() <- quit <- quit fmt.Println("Main program exits") } func testSynchronize_expense(money int) { lockChan <- 0 if(remainMoney >= money){ srcRemainMoney := remainMoney remainMoney -= money fmt.Printf("Originally%d, Spent%d,Surplus%d\n", srcRemainMoney, money, remainMoney) }else{ fmt.Printf("Want to consume%d Not enough money, Only left%d\n", money, remainMoney) } <- lockChan } func testSynchronize_gain(money int) { lockChan <- 0 srcRemainMoney := remainMoney remainMoney += money fmt.Printf("Originally%d, Earned%d,Surplus%d\n", srcRemainMoney, money, remainMoney) <- lockChan }
---------------------
Reference: https://blog.csdn.net/hesong1120/article/details/84326963