Golang Notes

static compilation

Compile-time A process of translating source code into a low-level language. The compilation process is slow, and when Go was designed, compilation speed was one of the main design goals. Static type means that the variable must specify a type, such as integer, string, boolean, array, etc. The variable type can be specified when the variable is declared. In most cases, the compiler can automatically infer the variable type.

garbage collection

Variables have a definite lifetime. For example, a local variable defined in a function, the variable does not exist when the function exits. The language's garbage collection mechanism records variables that are not in use, and then frees the memory they occupy. The garbage collection mechanism has some performance implications.

code runs

The go run command will compile and then run your code, compile the code in a temporary directory, execute it, and automatically clear the generated temporary files. If you just compile the code you can use go build.

variable assignment

The first way:

var power int
power = 9000

The second way:

var power int = 9000

The third way: used to declare a variable and assign a value to the variable. Go can infer the variable type. When declaring a variable for the first time, use :=, and the variable type is determined at this time. But then for assignments to this variable, use =.

power := 9000

gg := getPower()

func getPower() int{
    return 9001
}

The fourth way: go supports assignment of multiple variables to colleagues

name, power := "Goku", 9000

function declaration

Function supports multi-value return

no return value:

func log(message string){
}

A return value:

func add (a int, b int) int{
}

Two return values: func power(name string)(int,bool){ }There are many scenarios where multiple return values ​​are used. If you only want to get a certain value in the return value, you can assign the other return value to _:

_, exists:=power("goku")

if exists == false{
}

_ is a blank identifier, mostly used when there is no real assignment when returning a value, no matter what the return value is.

If the parameters of the function are all the same type, it can be defined concisely:

func add(a,b int) int{
}

structure

Unlike object-oriented languages, go has no concept of objects and inheritance. Therefore, there are not many features of object-oriented languages ​​such as polymorphism and overloading. go provides structures such as:

type Sanya struct{
    Name string
    Province int
}

Create a struct value type in a simple way:

goku := Sanya{
    Name : "sanya",
    Province :23,
}

Note that the comma at the end of the above structure cannot be omitted. When you don't need to set any value or even any field to the struct:

goku := Sanya{}

goku := Sanya{Name:"sanya"}
goku.Province = 23

Field names can also be omitted:

goku := Sanya{"sanya",23}

In most cases, we don't want a variable directly associated with a value, but want a pointer to point to the value of the variable, because in the go language, the parameter passing of functions is passed by copy. A pointer is a memory address. The actual value of this variable can be found through the pointer, which is an indirect value.

func main(){
    goku := &Sanya{"sanya",9000}
    Super(goku)
    fm.Println(goku.Power)
}

func Super(s *Sanya){
    s.Power = 10000
}

The result is 10000, so the pointer is passed. The overhead of copying a pointer variable is less than copying a complex structure.

Constructor

Structs don't have constructors, you can create a function that returns an instance of the corresponding type instead:

func NewSanya(name string, province int) Sanya{
    return Sanya{
        Name:name,
        Province:province,
    }
}

Allocate memory for newly created objects:

goku := &Sanya{
    name:"goku",
    province:23
}

Extend a defined pair structure:

type Sanya struct{
    Name string
    Province int
    Father *Sanya
}

initialization:

gohan := &Sanya{
    Name:"Sanya",
    Province:23,
    Father:&Sanya{
        Name:"Haiko",
        Province:23,
        Father:nil,
    }
}

Pointer types and value types

When you write go code, it is natural to ask yourself whether you should use value types or pointer types here. If you're not sure, use pointers. Pass-by-value is a way to ensure that data is immutable. Sometimes it is necessary to make changes to the calling code within the function, which requires the use of pointers. Even if you don't plan to change the data, consider the overhead of copying large structs if small structs can be copied.

array

Arrays are fixed size. When declaring arrays, you must specify their size. Once the size of the array is specified, it cannot be expanded.

var scores [10]int
scores[0] = 300

// 直接初始化一个有值的数组
scores := [4]int{9001,9002,9003,9004}

// 遍历数组
for index,value:= range scores{
}

Arrays are efficient but inflexible. When we process data, we generally do not know the number of elements, so we use slices.

slice

In go you generally rarely use arrays. Will use slices more. A slice is a lightweight structure encapsulation that, after encapsulation, represents a part of an array. Unlike creating an array when creating a slice, you do not need to specify a size.

scores := []int{1,2,3,4}

scores := make([]int,0,10) //长度为0但是容量为10的分片
scores := append(scores,5)

hash table

Define key-value pairs, which can be created by make:

lookup := make(map[stirng]int)
lookup["goku"] = 9001

package management

If you have installed git, execute the following command:

go get github.com/mattn/go-sqlite3 go get will get these remote files and save them in your workspace. Import the package into the workspace:

import(
    "github.com/mattn/go-sqlite3"
)

interface

Interface is a type, he only defines the declaration, no concrete implementation. Such as:

type Logger interface{
    Log(message string)
}

Interfaces enable decoupling in code.

Buffer in Go efficiently concatenates strings and customizes thread-safe Buffer:

In Go, you can use "+" to merge strings, but this method is very inefficient. Every time you merge, a new string is created, and the string must be traversed and copied. Strings can be efficiently concatenated through Buffer. Use bytes.Buffer to assemble strings without copying, just put the added strings at the end of the buffer. Since there is no trace of locks found in the write and read functions in the Buffer, the Buffer is not unsafe.

Go-specific concurrency programming model way:

Goroutine & Channel;

Coroutine Goroutine

In the Go world, each concurrently executed activity is called a goroutine. Through goroutine, parallel operation can be realized, which is very convenient. The go coroutine is similar to a thread, but the coroutine is scheduled by go itself, not the system. Code in a coroutine can be executed concurrently with other code.

func main(){
    fmt.Println("start")
    go process()
    time.Sleep(time.Millisecond * 10)
    fmt.Println("done")
}

func process(){
    fmt.Println("processing")
}

How do we start a coroutine pair. Just simply append the go keyword to the function to be executed. Go coroutines are easy to create with minimal overhead. Eventually multiple goroutines will run on the same underlying system thread. This is also often referred to as the M:N threading model because we have M application goroutines running on N system threads. As a result, the overhead of a go coroutine is relatively low compared to system threads (usually tens of K). On modern hardware, thousands of pairs of coroutines can be run. It also hides the complexity of mapping and scheduling. Concurrent execution is left to go to handle. The main thread does not wait for all the coroutines to complete execution before exiting, so the coroutines have a chance to execute before the main thread exits, so we must let the code cooperate.

Go high concurrent Http request

Goal: Be able to handle high volume POST requests from millions of endpoints. The HTTP request handler receives a JSON document containing many payloads. These payloads need to be written to Amazon S3, which is then processed by the map-reduce system.

We usually put the request into the queue, and form a worker pool with a certain number of goroutines (for example, the number of core CPUs), and the workers in the worker pool read the queue to execute tasks. Ideally, all cores of the CPU will be executed in parallel. Task.

Then set up two clusters, one for handling HTTP requests and one for workers. This can be scaled according to the workload of the processing background.

What does the main goroutine do?

  • Start the system detector;

  • Set general configuration, check the operating environment;

  • Create a timed garbage collector;

  • Execute the init function of the main package;

  • Execute the main function of the main package;

  • Do some aftercare work;

Synchronize

It is not difficult to create a coroutine, and it is not too expensive to start many coroutines. But concurrently executing code requires coordination. To solve this problem, go provides channels. The coroutine will split the code function into many assembly instructions. In the concurrent scenario, if you want to operate a variable safely, the only way is to read the variable. There can be as many reads as you want, but writes must be synchronized. True atomic operations that can depend on the cpu architecture. More often use a mutex.

//定义锁
lock sync.Mutex

//使用锁
lock.Lock()

//开锁
defer lock.Unlock()

Channel

The challenge of concurrent programming is data sharing. If your goroutines don't share data, you don't need to worry about them. However, in real-world scenarios, multiple requests are often required to share data. Channels are used to transfer data between go coroutines, and go coroutines can pass data to another go coroutine through channels. The result is that only one goroutine can access the data at any time.

  • Namely the channel type, one of Go's predefined types.
  • Typed, concurrency-safe general-purpose pipelines.
  • Used to pass data between multiple Goroutines.
  • The most direct embodiment of sharing memory by means of communication.

Channel's Happens before principle:

Send operation starts -> value copy (copy) -> send operation ends -> receive operation starts -> receiver holds value -> receive operation ends. Channel can coordinate the operation of multiple Goroutines.

Channels also have types, which is the type of data that will be passed to the channel, such as creating a channel that can be used to pass an integer:

c := make(chan int)

// 将这个通道传递给一个函数
fun worker(c chan int){
}

//通道发送数据
CHANNEL <- DATA

//通道接收数据
VAR := <-CHANNEL

The direction the tip is pointing is the direction in which the data flows. When we receive from or send data to a channel it blocks until there is data.

Define a data handler structure:

type Worker struct{
    id int
}

fun (w Worker) process(c chan int){
    for{
        data := <-c
        fat.Pringtf("worker data",w.id)
    }
}

Our worker is very simple and will wait for data until it is available, then process it, in a loop, dutifully waiting for more data and processing it forever.

Start multiple workers:

c := make(chan int)
for i:=0; I<4; I++{
    worker := Worker{id:i}
    go worker.process(c)
}

Create some tasks:

for{
    c <- rand.Int()
    time.Sleep(time.Millisecond*50)
}

We don't know which worker will get the data. But go can ensure that when sending data to a channel, only a single receiver can receive it. The channel provides all the synchronization code.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325334266&siteId=291194637