Go

effective_go

redeclare is ok

such as

1
2
3
f, err := os.Open(name)
...
d, err := f.Stat()

above such code, err used twice.

for

go use for instead of while and do while

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Like a C for
for init; condition; post { }

// Like a C while
for condition { }

// Like a C for(;;)
for { }

//If you're looping over an array, slice, string, or map, or reading from a channel, a range clause can manage the loop.
for key, value := range oldMap {
    newMap[key] = value
}

//If you only need the first item in the range (the key or index), drop the second:
for key := range m {
    if key.expired() {
        delete(m, key)
    }
}
//If you only need the second item in the range (the value), use the blank identifier, an underscore, to discard the first:
sum := 0
for _, value := range array {
    sum += value
}

defer

Defer

Deferred functions are executed in LIFO order when return

1
defer doSomething

data

make creates slices, maps, and channels only

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
func NewFile(fd int, name string) *File {
    if fd < 0 {
        return nil
    }
    f := new(File)
    f.fd = fd
    f.name = name
    f.dirinfo = nil
    f.nepipe = 0
    return f
}

can be instead by

1
2
3
4
5
6
func NewFile(fd int, name string) *File {
    if fd < 0 {
        return nil
    }
    return &File{fd, name, nil, 0}
}

print

printf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
func Printf(format string, v ...interface{}) (n int, err error) {}
// v is any number of any type 
// btw, below is the usage of ...
func Min(a ...int) int {
    min := int(^uint(0) >> 1)  // largest int
    for _, i := range a {
        if i < min {
            min = i
        }
    }
    return min
}

interface

Go type 不需要显示声明它实现了接口,仅通过实现接口来实现接口

1
2
3
4
5
6
7
8
9
// embedding 
type Job struct {
    Command string
    *log.Logger // as implement interface
}

func (job *Job) Printf(format string, args ...interface{}) {
    job.Logger.Printf("%q: %s", job.Command, fmt.Sprintf(format, args...))
}

concurrency

Do not communicate by sharing memory; instead, share memory by communicating.

add go before func to make it run concurrently

1
2
3
4
5
6
func Announce(message string, delay time.Duration) {
    go func() {
        time.Sleep(delay)
        fmt.Println(message)
    }()  // Note the parentheses - must call the function.
}

on above, go func is closures, how to sent signal? Use channels

channels

channels like the slide with semaphore.

1
2
3
4
// init
// if no buffer, <-ci wait until something -> ci
ci := make(chan int)   //no buffer
cj := make(chan int,10)//buffer

go func() 内存共享

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 不能确保 req 是唯一的
func Serve(queue chan *Request) {
    for req := range queue {
        sem <- 1
        go func() {
            process(req) // Buggy; see explanation below.
            <-sem
        }()
    }
}

//solve1: args sent to func
func Serve(queue chan *Request) {
    for req := range queue {
        sem <- 1
        go func(req *Request) {
            process(req)
            <-sem
        }(req)
    }
}
//solve2: redeclear
func Serve(queue chan *Request) {
    for req := range queue {
        req := req // Create new instance of req for the goroutine.
        sem <- 1
        go func() {
            process(req)
            <-sem
        }()
    }
}

server by channels

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
func handle(queue chan *Request) {
    for r := range queue {
        process(r)
    }
}

func Serve(clientRequests chan *Request, quit chan bool) {
    // Start handlers
    for i := 0; i < MaxOutstanding; i++ {
        go handle(clientRequests)
    }
    <-quit  // Wait to be told to exit.
}

go concurrency

涉及到并发 data race 都需要用锁。 如果不需要等待,那么可以用 mutex 若需要等待,

chan := make(channel bool) var wg sync.WaitGroup var mu sync.Mutex
cond := sync.NewCond(&mu) // 需要用到 broadcast 的地方才需要 cond,不然直接 chan

go channels: synchronous communication mechanism 如果是 unbuffer channels,那么无论是只有发送方还是只有接收方,都会造成阻塞,因为 channels 的本质是同步(buffer channel 也只会延迟这个过程)。

goroutin & mu

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# no race
func Lock_() {
	task := Task{
		mu:    sync.Mutex{},
		peers: nil,
	}
	for i := 0; i < 10000; i++ {
		task.peers = append(task.peers, i+i)
	}
	var wg sync.WaitGroup
	wg.Add(len(task.peers))
	task.mu.Lock()
	for i, i2 := range task.peers {
		go func(i int, i2 int) {
			defer wg.Done()
			task.mu.Lock()
			fmt.Printf("%v %v\n", i, i2)
			task.mu.Unlock()
		}(i, i2)
	}
	task.mu.Unlock()
	wg.Wait()
}

go gc

如果数组需要改动,将不需要的部分设为 nil,可以让 gc 识别到并回收。

go pprof

1
2
go test -run TestSpeed3A -memprofile mem.prof
go tool pprof -http=:8081 mem.prof
1
dstest -v2 -s -p10 -n10 -o log -r $(grep -oE '^func Test[A-Za-z0-9_]+' test_test.go | sed 's/^func //; s/(.*)//')
updatedupdated2024-04-122024-04-12