Go routine
Protocol: co-routine
Golang's handling of coprocesses:
- Protocol=> goroutine, several KB of memory, can be used in large quantities
- Flexible scheduling with frequent switching
GMP
Related data:
goroutine basic model and scheduler strategy in detail
Go has a M:N scheduler that can take advantage of multicore processors. At any time, M goroutine s need to be scheduled on N OS threads running on a maximum number of GOMAXPROCS processors.
G:goroutine protocol
P:processor processor processor (maximum GOMAXPROCS)
M:thread thread
There is a P-related local and global goroutine queue. Each M should be assigned to a P. If blocked or in system calls, P may not have M. At any time, there is at most one P in the number of GOMAXPROCS. At any time, only one M can run per P. If needed, more M can be created by the scheduler.
Scheduler Design Policies
Reuse Threads
- work stealing mechanism: underutilized processors actively look for threads from other processors and steal some
Handoff mechanism:
If a running protocol G1 Blocked, but other schedulers are handling their own business and don't have time to steal work stealing This blocked G1 With the other protocols in the queue above, we either wake up a thread or start a thread to switch our previous scheduler and other non-blocked processes over;G After the blocking is over, if you want to run it, put it on another scheduler P In the above protocol queue, if not executed, the M1 Thread can sleep or be destroyed
- Utilize Parallelism: Limit the number of P through GOMAXPROCS
- Preemption: Each goroutine can execute up to 10 ms (co-routine can only wait for CPU to actively release)
- Global G Queue: When P's local queue is empty, M first takes G from the global queue to P's local queue, and then steals it from other P's
Create goroutine
package main import ( "fmt" "time" ) //Sub go range func newTask(){ i := 0 for { i++ fmt.Printf("newTask Goroutine i=%d\n", i) time.Sleep(1 * time.Second) } } //Main go func main() { //Create a subroutine to execute newTask() go newTask() fmt.Println("main Goroutine exit")//After the main go, the subgo will be destroyed i := 0 for { i++ fmt.Printf("main Goroutine i=%d\n", i) time.Sleep(1 * time.Second) } }
Anonymous goroutine
package main import ( "fmt" "time" "runtime" ) func main() { go func () { defer fmt.Println("A.defer") func () { defer fmt.Println("B.defer") runtime.Goexit()//Exit the current go //return // Exit only the current closure, not the current go fmt.Println("B") }() fmt.Println("A") }() for { time.Sleep(1 * time.Second)//Dead cycle to prevent the main go from exiting } }
Anonymous goroutine with parameters
package main import ( "fmt" "time" ) func main() { go func (a int,b int) bool { fmt.Println("a=",a,",b=",b) return true }(10, 20) for { time.Sleep(1 * time.Second)//Dead cycle to prevent the main go from exiting } }
Pipeline channel
Define channel
package main import "fmt" func main() { c := make(chan int) go func () { c <- 666 //Send 666 to c }() num := <- c //Receive data from c and assign to num //Num, ok: = <-c //Check if the channel is closed or empty fmt.Println("num=", num)//num= 666 }
Note: If num does not receive data from c, the go will always be blocked
buffer
Unbuffered channel
c := make(chan int)
Buffered channel: When the channel is full, writing data to it will block; When channel is empty, fetching data will also block
c := make(chan int, 3) //Buffer capacity 3
package main import ( "fmt" "time" ) func main() { c := make(chan int, 3) go func () { defer fmt.Println("son go End of process") for i := 0; i < 4; i++ { c <- i fmt.Println("son go The process is running i=",i) } }() time.Sleep(1 * time.Second) for i := 0; i < 4; i++ { num := <- c fmt.Println("num=", num) } }
Turn off channel
- Channels don't shut down as often as files. They do not shut down channel s unless there is really no data to send or if you want to explicitly end range loops.
- Cannot send data to channel after channel is closed (cause receive to return zero immediately after panic error is raised)
- After closing the channel, you can continue to receive data from the channel
- For nil channel, both send and receive will be blocked
package main import "fmt" func main() { c := make(chan int, 3) go func () { for i := 0; i < 5; i++ { c <- i } close(c)//Turn off channel }() for { //ok for true means channel is not closed (or has data), false means no data and is closed if data, ok := <- c; ok { fmt.Println(data) }else{ break } } fmt.Println("main End") }
channel and range
range can iterate over and over channel s (blocking)
package main import "fmt" func main() { c := make(chan int) go func () { for i := 0; i < 10; i++ { c <- i } close(c)//Turn off channel }() for data := range c { fmt.Println(data) } }
channel and select
Only one channel state can be monitored by the next go in a single process, and select can complete the monitoring of multiple channels (typically with a for dead loop)
select { case <- chan1: //If chan1 successfully reads data (readable), perform the case processing case chan2 <- 1://If chan2 writes successfully (writable), perform the case processing default: //If none of the above succeeds, default processing is performed }
case
package main import "fmt" func test(c, quit chan int){ x, y := 1, 1 for { select { case c <- x://c can be written, then execute the case x = y y = x + y case <- quit://quit has a value, then execute the case fmt.Println("quit") return } } } func main() { c := make(chan int) quit := make(chan int) //Sub go range go func () { for i := 0; i < 10; i++ { fmt.Println(<-c)//Read data from c, otherwise block } quit <- 0 }() //Main go test(c, quit) } Result: 1 1 2 4 8 16 32 64 128 256 quit
Note: Unbuffered channel reads and writes will block
Go Modules
GOPATH pull dependency
go get -u xxx
Disadvantages of GOPATH
- There is no version control concept
- Unable to synchronize consistent third-party version numbers
- The third-party version number referenced by the current project cannot be specified
Go Modules mode
command | Effect |
---|---|
go mod init | Generate go.mod file |
go mod download | Download all dependencies specified in the go.mod file |
go mod tidy | Organize existing dependencies |
go mod graph | View existing dependency structures |
go mod edit | Edit go.mod file |
go mod vendor | Export all dependencies of the project to the vendor directory |
go mod verify | Verify whether a module has been tampered with |
go mod why | See why you need to rely on a module |
environment variable
View through go env
GO111MODULE
Switch on GO Modules, recommended to enable
Open mode:
go env -w GO111MODULE=on
GOPROXY
Set up Go module proxy
Set up Qiniuyun agent:
go env -w GOPROXY=https://goproxy.cn,direct
direct return source: if the agent cannot find it, it will pull from the source
GOSUMDB
Ensure that the pulled module version data is not tampered with (recommended to open)
GONOPROXY/GONOSUMDB/GOPRIVATE
Configure private libraries (GOPRIVATE is recommended directly)
go env -w GOPRIVATE="*.example.com"
Go Modules Initialization Project
Create go.mod
go mod init Current module name(Use when importing packages)
Import package (download path: $GOPATH/pkg/mod)
go get Package address (example: github.com/aceld/zinx/znet)
go.sum: Lists all module versions that the current project depends on directly or gradually, ensuring that future versions of the project depend on are not tampered with
Example:
package main import ( "fmt" "github.com/aceld/zinx/ziface" "github.com/aceld/zinx/znet" ) //ping test custom routing type PingRouter struct { znet.BaseRouter } //Ping Handle func (this *PingRouter) Handle(request ziface.IRequest) { //Read client data first fmt.Println("recv from client : msgId=", request.GetMsgID(), ", data=", string(request.GetData())) //Write back ping...ping...ping err := request.GetConnection().SendBuffMsg(0, []byte("ping...ping...ping")) if err != nil { fmt.Println(err) } } func main() { //1 Create a server handle s := znet.NewServer() //2 Configure Routing s.AddRouter(0, &PingRouter{}) //3 Open Services s.Serve() }
Modify package dependencies
go mod edit -replace=xxx