golang quick method

Posted by FrozNic on Sat, 08 Jan 2022 14:05:54 +0100

1. go Language Overview

go core programming direction:

  • Blockchain R & D Engineer
  • go server / Game Software Engineer
  • go distributed / Cloud Computing Software Engineer

Advantages of go:

  • data processing
  • High concurrency

Why did google create go language:

  • The hardware technology is updated frequently and the performance is improved rapidly. The existing language can not make rational use of the advantages of multi-core and multi CPU
  • The computing power of the existing language is not enough, and the processing of large concurrency is not good enough
  • Want to take into account the running speed and development speed

Brief history of development:

  • In 2015, go1 5 release, removing the last remaining c code
  • In 2017, go1.0 was released successively 8 and 1.9
  • In 2018, go1.0 was released Version 10

go features:

  • go=c+python
  • It inherits many ideas from c language, such as pointer
  • Automatic garbage collection
  • Natural concurrency
    • Support concurrency from the language level
    • goroutine, a lightweight thread, can realize large concurrent processing and make efficient use of multi-core
    • Implementation of concurrency model based on CPS
  • Communication between different goroute s is realized through pipes
  • Function can return multiple values
  • New syntax: delay execution defer, slice slice, etc

2. Basic grammar

1. Variable usage

  1. Specify the variable type. If no value is assigned after declaration, the default value will be used
  2. Automatically derive types from values
  3. Even var is omitted, but use the: = character, which is equivalent to declaration + assignment
var i int
fmt.Println("i=",i)

var j = "hello"
fmt.Println("j=",j)

k := 10.5
fmt.Println("k=",k)

Note: the main function can run only after it is placed under the main package, as shown in the following figure.

2. Declare multiple variables at once

	var i,j,k int
	fmt.Println("i=",i,"j=",j,"k=",k)

	var o,p,q = 10,"hello",10.5
	fmt.Println("o=",o,"p=",p,"q=",q)

	r,s,t := 10,"hello",10.5
	fmt.Println("r=",r,"s=",s,"t=",t)

3. Declare global variables

package main

var n1 = 100
var n2 = "okk"
var (
	n3 = 36.9
	n4 = "yes"
)

func main() {
}

4. Constant

const a int = 10

Constants must be initialized

Constants can only modify bool, numeric type (int, float series) and string type

5. go data type

Basic data type

  • Numerical type
    • Integer type (int,int8,int16,int32,int64,uint,uint8,uint16,uint32,uint64)
    • Floating point type (float32,float64)
  • Character type (there is no special character type, and byte is used to save single alphabetic characters)
  • Boolean (bool)
  • character string

Derived / complex data types

  • Pointer
  • array
  • structural morphology
  • The Conduit
  • function
  • Slice (dynamic array)
  • Interface
  • map

6. Value type and reference type

Value type: basic data type, array, structure

Reference types: pointer, slice, map, pipe, interface

7. Operator

The self increment and self decrement of golang can only be used as independent statements, not b:=a++

golang does not have + + i

3. go characters and strings

1. Differences between go characters

For other languages, strings consist of a string of characters, while go strings consist of a string of bytes.

    var b1 byte = 'a'
	fmt.Println("b1=",b1) // Output 97

	var b2 byte = 97
	fmt.Printf("b2=%c\n",b2) // Output a

	b3 := 'you'
	fmt.Println("b2=",b3) // Output unicode code value

The following test character and Boolean types occupy a few bytes respectively.

b := 'a'
success := false

fmt.Println(unsafe.Sizeof(b)) // Output 4
fmt.Println(unsafe.Sizeof(success)) // Output 1

If the number of bytes encoded in UTF-8 of a character is greater than 1, byte cannot be saved, but int is no problem. As shown in the figure below.

2. Basic use of strings

    var s1 = "hello"+"world" // Splicing
	s1+="hi"
	// Because go can not end with a semicolon, when the string splicing statement is too long and needs to wrap, the end of each line must end with a + sign
	s2 := "I am"+"One"+
		"Chinese"+"hello"+
		"world"
	s3 := `Output the string as is, which is suitable for outputting source code, etc.\n\t`
	fmt.Println(s1)
	fmt.Println(s2)
	fmt.Println(s3)

3. Display type conversion between basic data types

    var a int = 9999
	var b byte = byte(a)
	fmt.Println(b)

4. Conversion between String and basic type

The first method is to convert the basic type to string, as shown in the following code.

    var a int = 100
	s := fmt.Sprintf("%d", a) // Use the Sprintf function for conversion
	fmt.Printf("type=%T,value=%q",s,s) // Output the type and value of% s,%q one more double quotation mark than the output result of% s

The basic type is converted to string. The second method is shown in the following code.

    var a int = 100
	s := strconv.FormatInt(int64(a), 2)
	fmt.Printf("type=%T,value=%q",s,s)

	// f indicates format, 10 indicates decimal places, 10 digits are reserved, and 64 indicates that the decimal place is float64
	s = strconv.FormatFloat(12.99, 'f', 10, 64)
	fmt.Printf("type=%T,value=%q",s,s)

string to the basic type, as shown in the following code.

    b, err := strconv.ParseBool("fals1")
	// b, _ := strconv.ParseBool("true") / / if you don't want to get err information, use_
	fmt.Printf("%v",b) // If it cannot be converted to a valid value, it will be converted to the default value, and so will string to other types
	fmt.Println(err)

4. Get user input

	// Mode 1
	var name string
	var age int
	// fmt.Scanln(&name)
	// fmt.Println("your name is:", name)

	// Mode 2
	fmt.Scanf("%s %d",&name,&age)
	fmt.Println("Your name is:",name,",Your age is:",age)

5. Generate random number

	// Set seed
	rand.Seed(time.Now().Unix())
	n:=rand.Intn(100)+1
	fmt.Println(n)

6. Pointer

    // Output address of basic type
	i := 10
	fmt.Println(&i)

	// Declare a pointer of type * int, and the value of p itself is & I
	var p *int = &i
	fmt.Println(p) // Output the value of p itself
	fmt.Println(&p) // Address of output p
	fmt.Println(*p) // Output the value pointed to by p

7. Process control statement

1. if and switch

	if 5>3 {
		fmt.Println("Hello")
	}

	// Supports defining variables in if
	if age:=10;age>9 {
		fmt.Println("hello")
	}

	score:=30
	switch score {
	case 10,20: fmt.Println("Bad") // Auto break
	case 30,40: fmt.Println("it 's not bad"); fallthrough // Only one layer is penetrated by default
	default: fmt.Println("Go 3")
	}

	// This kind of writing is similar to if else
	switch {
	case score<10 || score>90:fmt.Println("weirdo")
	default:fmt.Println("Regular")
	}

	var service interface{}
	var y = 10
	service = y
	switch service.(type) {
	case nil:fmt.Println("Empty type")
	case int:fmt.Println("int type")
	case float64:fmt.Println("float64 type")
	}

2. for loop

    for i := 1; i < 10; i++ {
		fmt.Println("hello")
	}
	// It is equivalent to a while loop. There is no while keyword in go
	j:=1
	for j<10{
		fmt.Println("hi")
		j++
	}
	// Two ways of writing infinite loop
	// for ;; {} or for {}

	// For range, traversable strings and arrays
	// The traditional way to traverse the string is to traverse by bytes, which cannot contain Chinese
	s:="hello,world in"
	for i:=0;i<len(s);i++{
		fmt.Printf("%c\n",s[i])
	}
	// For range is traversed by characters
	for index,item:=range s{
		fmt.Printf("%d-%c\n",index,item)
	}
	// Turning a string into a slice can also traverse by character
	s2 := []rune(s)
	for i:=0;i<len(s2);i++{
		fmt.Printf("%c\n",s2[i])
	}

3. goto statement

	label:
	fmt.Println("goto test1")
	fmt.Println("goto test2")
	goto label

8. Functions

1. Function core

Syntax:

func Function name (parameter list ) (Return value type list){
}

Note:

  • Placeholders can be used when receiving multiple return values_ Ignore a return value.

  • If there is only one return value, the return value type list may not be written.

  • The go function does not support overloading.

  • The parameter transfer of go function is value transfer. If you want to modify the variable value in the function, you can pass in the variable address and operate the variable with a pointer in the function.

Functions are also data types that can be assigned to variables, and functions can be used as formal parameters. The following code is shown.

	// Define anonymous functions and assign values to variables
	mysum := func(a int,b int) int {
		return a+b
	}
	fmt.Printf("call mysum[%T][%d]",mysum,mysum(10,20))

User defined data types: type myint int, type mysum func(int,int) int, which is equivalent to alias. The following code is shown. This grammar is of little use

	type lensum func(s1 string,s2 string)int
	var totallen lensum
	totallen = func(s1 string,s2 string) int {
		return len(s1)+len(s2)
	}
	fmt.Println("Custom type:",totallen("hello","hi"))

It also supports naming function return values, as shown in the following code. This grammar is of little use

func sum(a int,b int)(sum int,sub int){
	sum = a+b
	sub = a-b
	return
}

Variable parameters:

// args is a slice that can be accessed by index
func sum2(args... int) int {
	var sum int
	for i := 0; i < len(args); i++ {
		sum +=args[i]
	}
	return sum
}

Initialization function:

// Each source file can define an init function, which is executed before the main function and called by the go run framework
func init(){
   fmt.Println("Test_07_function.go Initialized")
}

Function closure:

	initClickCnt := func(initCnt int) func(int){
		totalCnt := initCnt
        // totalCnt and cnt constitute the closure of the function. totalCnt is initialized only once, which is equivalent to a global variable, while cnt is equivalent to a local variable
		return func(cnt int) {
			totalCnt += cnt
			fmt.Println("Current hits:",totalCnt)
		}
	}
	printClickCnt := initClickCnt(100)
	printClickCnt(8) // 108
	printClickCnt(10) // 118
	printClickCnt(2) // 120	

defer deferred execution:

	deferTest := func() {
		// Relevant statements enter the defer stack. After the method is completed, the statements in the defer stack are executed in a later in first out manner. It can be used to close file handles, database connections, locks and other resources.
		defer fmt.Println("Close a resource")
		defer fmt.Println("Close a handle")
		fmt.Println("Execution logic")
	}
	deferTest()

2. System function

Time and date correlation functions:

	now := time.Now() // Get current time
	fmt.Printf("%02d-%02d-%02d %02d:%02d:%02d\n",now.Year(),now.Month(),now.Day(),now.Hour(),
		now.Minute(),now.Second())
	fmt.Println(now.Format("2006-01-02 15:04:05")) // It must be written like this
	time.Sleep(3*time.Second) // Sleep for 3 seconds

3. Built in function

len() finds the length of string and array

new() is used to allocate memory. It is mainly used to allocate value types (fill in default values) and return pointers. The following code is shown.

	p := new(int)
	fmt.Printf("type[%T]Pointer value[%v]Pointer address[%v]The value pointed to by the pointer[%v]The type of value the pointer points to[%T]",p,p,&p,*p,*p)	

make() is used to allocate memory, mainly to allocate reference types, and returns pointers

9. go language specification

1. Package specification

If you want to compile into an executable file, you need the main package. If you just write a library, you don't need the main package.

Execute the following command in gopath directory and compile it into an executable file:

// When compiling, you need to compile the folder where the main package is located
// The executable file generated after compilation can be in the gopath directory. You can also specify the path and file name through the - o parameter
go build -o bin/my.exe project_name/package_name/main

When import ing a package, the path is written from GOPATH/src without src.

There are multiple main functions under the main package. How to ignore them during compilation?

  • Just add / / + build ignore at the top of the file where the main function to be ignored is located and leave at least one blank line.

2. Identifier specification

Try to match the package name with the directory name

If the variable name, function name and constant name are capitalized, they are public; otherwise, they are private

10. go language exception handling

func main() {
	// Test exception handling
	test()
	// Custom error
	customError := func(s string) error{
		if(s == "Xiao Ming"){
			return nil
		}else{
			return errors.New("Parameter value is not Xiao Ming!")
		}
	}
	if err := customError("Xiao Zhang");err!=nil {
		panic(err)
	}
}

func test() {
	defer func() {
		if err:=recover();err != nil{
			fmt.Println("test An exception occurred in the method")
		}
	}() // Write () to execute it
	a:=10
	b:=0
	c:=a/b
	fmt.Println(c)
}

11. Arrays and slices

1. Array

	// Several ways to define arrays
	// Mode 1
	var arr [5]int
	arr[0] = 2
	fmt.Println(arr[0])
	// Mode 2
	var arr2 = [5]int{1,2,3,4,5}
	fmt.Println(arr2[0])
	// Mode 3
	var arr3 = [...]int{1,2,3}
	fmt.Println(arr3[0])
	//Mode 4, if you write Array type, otherwise slice
	var arr4 = [...]string{1:"Guan Yu",0:"Liu Bei",2:"Zheng Fei"}
	fmt.Printf("%T",arr4)

2. Slice

	// Define slice
	// Method 1: reference array. If startIndex is 0 or endIndex is the array length, it can be omitted, such as arr [:]
	var slice []int = arr2[1:4]
	fmt.Println("Value of slice:",slice)
	fmt.Println("Number of elements sliced:",len(slice))
	fmt.Println("Slice capacity:",cap(slice))
	// Mode 2
	var slice2 []int = make([]int,4,16)
	fmt.Println("Value of slice:",slice2)
	// Method 3: direct reference array
	var slice3 []int = []int{1,2,3,4,5}
	fmt.Println("Value of slice:",slice3)
	// Slice dynamic growth
	slice3 = append(slice3,6)
	fmt.Println("Value of slice:",slice3)
	// Slice copy (array not available)
	var slice4 []int = make([]int,10,10)
	copy(slice4,slice3)
	fmt.Println(slice4)

3. string and slice

The bottom layer of string is byte array, so it can also be sliced

	// String can be converted into slice or [] byte
	s := "hello China"
	slice5 := []rune(s)
	for _,item:=range slice5 {
		fmt.Printf("%c",item)
	}

4. Multidimensional array

	var arrtwo [3][4]int = [3][4]int{{1,2,3,4},{5,6,7,8},{9,10,11,12}}
	fmt.Println("\n",arrtwo)

12,Map

key type:

  • Yes: bool, number, string, pointer, channel, interface, structure, array, etc
  • No: slice, map, function, because these cannot be judged with = =
	// map can be dynamically expanded
	var wordcnt map[string]int = make(map[string]int,10)
	wordcnt["Guan Yu"] = 20

	// Initialize when declared
	var wordcnt2 map[string]int = map[string]int{
		"Fei Zhang":20,
		"Zhao Yun":16,
		"Cao Cao":32,
	}
	fmt.Println(wordcnt2)

	//Delete a key. If you want to delete all keys, you can use the make function to allocate new memory
	delete(wordcnt2,"Fei Zhang")

	// lookup
	value,b := wordcnt2["Cao Cao"]
	fmt.Println(value,b)

	// Traversal, only for range can be used
	for k,v:=range wordcnt2 {
		fmt.Println(k,"=",v)
	}

	// map slice
	var mapslice []map[string]int = make([]map[string]int,2)
	// Dynamic increase
	element := map[string]int{
		"hello":5,
		"ok":8,
	}
	mapslice = append(mapslice,element)
	fmt.Println(mapslice)

13. go language object oriented programming

1. Structure struct

Note:

  • struct is similar to class.
  • go object-oriented programming is very concise, without inheritance, method overloading, constructor, destructor, hidden this pointer and so on.
  • go still has encapsulation, inheritance and polymorphism, but the implementation methods are different. For example, inheritance does not have the extends keyword, and inheritance is implemented through anonymous fields (combinations).
  • Interface oriented programming is a very important feature of go.
  • The address of the structure attribute is allocated consecutively
  • When two different structures are converted, the corresponding fields need to be the same
  • You can give each field a tag, which will be used in serialization
  • Because the go method acts on the specified data type, it can also have methods not only struct, int, float, etc
  • Type can implement string method (equivalent to toString method in Java)
type User struct {
	name string `json:"name"`
	age int `json:"age"`
	password string `json:"password"`
}

func (u *User) addAge(b int) {
	// The structure is a value type and is a value copy when parameters are passed. Therefore, the properties of the original struct cannot be modified
	// If you want to modify the properties of the original struct, you need to pass in the pointer, * User, and the underlying layer is optimized. You can replace (* U) with u.age age
	u.age = u.age+b
}

func main() {
	// Method 1: declare structure variables and assign values
	var u1 User = User{"Zhang San",10,"123456"}
	fmt.Println(u1)
	// Mode 2:
	var u2 *User = new(User)
	(*u2).name = "Li Si"
	u2.password = "ok" // It can also be written because: conversion is made at the bottom
	fmt.Println(*u2)
	// Mode 3
	var u3 *User = &User{}
	u3.name="Wang Wu"

	// Call method
	u1.addAge(5)
	fmt.Println(u1.age)
}

2. Inherit

Inheritance is implemented through anonymous fields.

type Animal struct {
	name string
	age int
}
type Dog struct {
	Animal
	owner string
}

func main() {
	dog := Dog{}
	dog.owner = "Zhang San"
	dog.name = "Labrador"
	dog.age = 1
	fmt.Println(dog)
}

Note:

  • You can inherit all fields and methods, including private
  • When the parent and child contain fields or methods with the same name, the proximity principle is adopted
  • When multiple inheritance occurs, and there are fields or methods with the same name between multiple parent structures, you must specify which parent structure it is

3. Interface and polymorphism

type UserService interface {
	get() string
	insert() int
}

type UserServiceImpl struct {

}

func (service UserServiceImpl) get() string {
	fmt.Println("implement get method")
	return "name:Zhang San;age:10"
}
func (service UserServiceImpl) insert() int {
	fmt.Println("implement insert method")
	return 8
}

func doService(service UserService) {
	fmt.Println(service.get())
	fmt.Println(service.insert())
}
func main() {
	doService(UserServiceImpl{})
}

Note:

  • go does not have the implements keyword
  • Only one user-defined type is required to implement the interface, and then all methods in the interface can be implemented
  • The empty interface interface {} does not have any method, because it is considered that all types implement the interface, which is equivalent to the total Object class of Java
  • Variables cannot be defined in an interface
  • Interfaces can inherit
  • The polymorphism in go is implemented through the interface and supports polymorphic parameters and polymorphic arrays

4. The difference between type assertion and forced conversion

	// The difference between type assertion and forced conversion
	var b UserService
	var a interface{} = UserServiceImpl{}
	// Strong turn. An error is reported. Because the specific type of a is unknown, it cannot be forcibly converted
	// b = UserService(a) //
	// Type Asserts 
	b = a.(UserService)
	doService(b)
	// Type assertion with detection
	c,ok:=a.(UserService)
	if ok {
		doService(c)
	}

14. File operation

1. Basic use

// Open file
	file, _ := os.OpenFile("F:/tmp/test.txt",os.O_RDONLY,os.ModePerm)
	// Close file
	defer file.Close()
	// Loop read file
	reader := bufio.NewReader(file)
	for{
		line, err2 := reader.ReadString('\n')
		if err2 == io.EOF {
			// Indicates that the end of the file has been read
			break
		}
		fmt.Println(line)
	}
	
	// One time read file
	content, _ := ioutil.ReadFile("F:/tmp/test.txt")
	fmt.Println(content)

	// Write file
	writer := bufio.NewWriter(file)
	writer.WriteString("hello")
	writer.Flush()
	
	// Determine whether the file exists
	_, err := os.Stat("F:/tmp/test.txt")
	if err == nil {
		fmt.Println("existence")
	}else if os.IsNotExist(err){
		fmt.Println("non-existent")
	}else{
		fmt.Println("uncertain")
	}
	
	// File copy
	io.Copy(writer,reader)

2. Case

1. Split file

Description: divides a large file into n small files

Parameters:

  • inFilePath large file path
  • outFilePath small file storage directory
  • splitLineNum line number array, such as [2,4,6], will be cut on lines 2, 4 and 6 of the large file respectively, and finally four small files will be generated
func Split(inFilePath string,outFilePath string,splitLineNum []int)  {
	file, err := os.OpenFile(inFilePath, os.O_RDONLY, os.ModePerm)
	suffix := path.Ext(inFilePath)
	defer file.Close()
	if nil != err{
		fmt.Println("File opening failed!")
		return
	}
	reader := bufio.NewReader(file)

	// initialize variable
	cnt := 0
	index := 0
	num := splitLineNum[index]
	splitFile,_ := os.OpenFile(outFilePath+"/split_"+fmt.Sprintf("%d",index)+suffix, os.O_APPEND|os.O_CREATE, os.ModePerm)
	writer := bufio.NewWriter(splitFile)

	defer  splitFile.Close()
	for {
		line, err := reader.ReadString('\n')
		if io.EOF == err {
			break
		}
		// Judgment is enough to read the dividing line
		if cnt == num {
			// Be sure to flush before closing the file
			writer.Flush()
			splitFile.Close()
			// Change to the next file
			index++
			if index < len(splitLineNum) {
				num = splitLineNum[index]
			}
			splitFile,_ = os.OpenFile(outFilePath+"/split_"+fmt.Sprintf("%d",index)+suffix, os.O_APPEND|os.O_CREATE, os.ModePerm)
			writer = bufio.NewWriter(splitFile)
		}
		writer.WriteString(line)
		cnt++
	}
	writer.Flush()

}

2. Calculate sub file size

Description: enter a path and output the size of all sub files under the path. By default, the file size is sorted from large to small

Parameters:

  • inFilePath input path
  • unit
func calculateChildFileSize(inFilePath string) int64{
	stat, _ := os.Stat(inFilePath)
	file, _ := os.OpenFile(inFilePath, os.O_RDONLY, os.ModePerm)
	defer file.Close()
	if stat.IsDir() {
		childs, _ := file.Readdir(-1)
		var total int64
		for _,item := range childs{
			total += calculateChildFileSize(inFilePath + "/" + item.Name())
		}
		return total
	}else{
		return stat.Size()
	}
}
func ChildFileSize(inFilePath string,unit string){
	file, _ := os.OpenFile(inFilePath, os.O_RDONLY, os.ModePerm)
	defer file.Close()
	childs, _ := file.Readdir(-1)
	var fileSizeMap map[int64]string = make(map[int64]string,10)
	for _,item := range childs{
		fileSizeMap[calculateChildFileSize(inFilePath+"/"+item.Name())] = item.Name()
	}
	var keys []int
	for k,_ := range fileSizeMap {
		keys = append(keys,int(k))
	}
	sort.Ints(keys)
	for i:=len(keys)-1;i>=0;i--{
		if unit == "KB" {
			fmt.Println(fileSizeMap[int64(keys[i])],"\t",keys[i]/1024,"KB")
		}else if unit == "MB" {
			fmt.Println(fileSizeMap[int64(keys[i])],"\t",keys[i]/(1024*1024),"MB")
		}else{
			fmt.Println(fileSizeMap[int64(keys[i])],"\t",keys[i],"B")
		}
	}
}

3. File synchronization

Description: synchronize files from one place to another. The default synchronization mode is overwrite. However, files with destination but no source will not be deleted

Parameters:

  • dst: destination path
  • src: source file or source folder
func Sync(dst string,src string) {
	// Determine whether it is a folder, assuming that src exists
	stat, _ := os.Stat(src)
	if !stat.IsDir() {
		f1, _ := os.OpenFile(src, os.O_RDONLY, os.ModePerm)
		if _, err := os.Stat(dst+"/"+path.Base(src));nil == err || os.IsExist(err){
			// If it exists, delete the previous file
			os.Remove(dst+"/"+path.Base(src))
		}
		f2, _ := os.OpenFile(dst+"/"+path.Base(src), os.O_RDWR|os.O_CREATE, os.ModePerm)
		defer f1.Close()
		defer f2.Close()
		io.Copy(f2,f1)
	}else {
		if _, err := os.Stat(dst+"/"+path.Base(src));nil != err && os.IsNotExist(err){
			// If it does not exist, create a folder
			os.Mkdir(dst+"/"+path.Base(src),os.ModePerm)
		}
		f, _ := os.OpenFile(src, os.O_RDONLY, os.ModePerm)
		childs, _ := f.Readdir(-1)
		for _,item := range childs{
			Sync(dst+"/"+path.Base(src),src+"/"+item.Name())
		}
	}
}

15. Command line parameters

You can use OS Args gets all command line parameters.

More conveniently, you can specify a parameter name (such as - name). When you specify a parameter name, you can obtain the parameter in the following way.

var name string
flag.Stringvar(&name,"name","Default value","user name")
flag.Parse()

16. Serialization and deserialization

type Student struct {
	Name string `json:"name"`
	Age int `json:"age"`
	Score int `json:"score"`
}
// serialize
	stu := Student{"Zhang San",23,98}
	jsonStr, _ := json.Marshal(stu)
	fmt.Println(string(jsonStr))
// Deserialization
	var stu2 Student
	_ = json.Unmarshal([]byte(jsonStr), &stu2)
	fmt.Println(stu2)

17. goroutine and channel

1. goroutine overview

go can easily build tens of thousands of collaborative processes because it is optimized at the bottom, which can be understood as a more lightweight thread

go collaboration features:

  • Independent stack space
  • Shared program heap space
  • Scheduling is controlled by users

If the main thread exits, the coroutine will exit even if it has not finished executing.

The main thread is a physical thread that directly acts on the CPU. It is a heavyweight and consumes CPU resources.

The collaboration process is started from the main thread. It is a lightweight thread. It is in a logical state and consumes relatively little resources.

Other programming languages are based on threads. Opening too many threads consumes a lot of resources, which makes go have great concurrency advantages.

2. goroutine concurrency model

MPG mode:

  • M: The main thread of the operating system (which is a physical thread)
  • P: Context required for collaborative process execution
  • G: Synergetic process

A program can have multiple Ms. when multiple MS run on different CPU s, they are parallel.

Each M corresponds to a P, a running G and a co process waiting queue.

How to start a collaborative process?

go test()

3. Concurrent security

When there is a concurrent security problem, compile with the - race parameter, and then execute the exe file. The result is correct.

How to ensure thread safety:

  • lock

    var lock sync.Mutex
    lock.Lock()
    lock.Unlock()
    
  • channel (communication between different goroutine s)

4. channel overview

A channel is essentially a queue.

When multiple goroutine s access the channel, there is no need to lock it. The channel itself is thread safe.

The channel has types, and the string channel can only store string types.

Basic use of channel:

	// Declare channel
	var intChan chan int
	intChan = make(chan int,10)
	// Write data. If you write too much, you will report an error
	intChan<-10
	intChan<-20
	intChan<-30
	intChan<-40
	// This capacity will not grow dynamically
	fmt.Println(len(intChan),cap(intChan))
	// Read the data and report an error if you read too much
	ele := <-intChan
	fmt.Println(ele)
	// Close the pipeline. After closing, you can only read but not write
	close(intChan)
	// The channel can be traversed, but if the channel is not closed, an error will be reported. Otherwise, the channel can be traversed normally
	for item:=range intChan{
		fmt.Println(item)
	}

matters needing attention:

  • channel can be declared read-only or write only.
  • Using select can solve the blocking problem of reading data from the pipeline.
  • Using recover in goroutine can avoid goroutine exiting because of panic.

18. Reflection

The type of the variable (reflect.Type interface) can be obtained through reflection (reflect.TypeOf function).

The value of the variable (reflect.Value structure) can be obtained through reflection (reflect.ValueOf function).

19. Network programming

func server() {
	server, err := net.Listen("tcp", "0.0.0.0:8888")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer server.Close()
	fmt.Println("The server is started successfully!")
	for {
		client, _ := server.Accept()
		fmt.Println("Client connection succeeded!")
		var data []byte = make([]byte,128)
		client.Read(data)
		fmt.Println("The server received the message:",string(data))
	}
}
func client() {
	client, _ := net.Dial("tcp", "localhost:8888")
	defer client.Close()
	data := []byte("Hello, golang!")
	client.Write(data)
}

func main() {
	go server()
	time.Sleep(1*time.Second)
	client()
}

Topics: Go Back-end