👉腾小云导读
var client *http.Client
if tracing {
client, err := createClientWithTracing()
if err != nil {
return err
}
log.Println(client)
} else {
client, err := createDefaultClient()
if err != nil {
return err
}
log.Println(client)
}
var client *http.Client
var err error
if tracing {
client, err = createClientWithTracing()
} else {
...
}
if err != nil { // 防止重复代码
return err
}
go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
go vet -vettool=C:\Users\luozhiyun\go\bin\shadow.exe .\main.go
# command-line-arguments
.\main.go:15:3: declaration of "client" shadows declaration at line 13
.\main.go:21:3: declaration of "client" shadows declaration at line 13
package main
import "fmt"
var a = func() int {
fmt.Println("a")
return 0
}()
func init() {
fmt.Println("init")
}
func main() {
fmt.Println("main")
}
// output
a
init
main
var db *sql.DB
func init(){
dataSourceName := os.Getenv("MYSQL_DATA_SOURCE_NAME")
d, err := sql.Open("mysql", dataSourceName)
if err != nil {
log.Panic(err)
}
db = d
}
type Foo struct {
Bar
}
type Bar struct {
Baz int
}
type Logger struct {
writeCloser io.WriteCloser
}
func (l Logger) Write(p []byte) (int, error) {
return l.writeCloser.Write(p)
}
func (l Logger) Close() error {
return l.writeCloser.Close()
}
func main() {
l := Logger{writeCloser: os.Stdout}
_, _ = l.Write([]byte("foo"))
_ = l.Close()
}
type Logger struct {
io.WriteCloser
}
func main() {
l := Logger{WriteCloser: os.Stdout}
_, _ = l.Write([]byte("foo"))
_ = l.Close()
}
type InMem struct {
sync.Mutex
m map[string]int
}
func New() *InMem {
return &InMem{m: make(map[string]int)}
}
func (i *InMem) Get(key string) (int, bool) {
i.Lock()
v, contains := i.m[key]
i.Unlock()
return v, contains
}
m := inmem.New()
m.Lock() // ??
|
type options struct {
port *int
}
type Option func(options *options) error
func WithPort(port int) Option {
// 所有的类型校验,赋值,初始化啥的都可以放到这个闭包里面做
return func(options *options) error {
if port < 0 {
return errors.New("port should be positive")
}
options.port = &port
return nil
}
}
func NewServer(addr string, opts ...Option) (*http.Server, error) {
var options options
// 遍历所有的 Option
for _, opt := range opts {
// 执行闭包
err := opt(&options)
if err != nil {
return nil, err
}
}
// 接下来可以填充我们的业务逻辑,比如这里设置默认的port 等等
var port int
if options.port == nil {
port = defaultHTTPPort
} else {
if *options.port == 0 {
port = randomPort()
} else {
port = *options.port
}
}
// ...
}
server, err := httplib.NewServer("localhost",
httplib.WithPort(8080),
httplib.WithTimeout(time.Second))
server, err := httplib.NewServer("localhost")
sum := 100 + 010
fmt.Println(sum)
file, err := os.OpenFile("foo", os.O_RDONLY, 0644)
file, err := os.OpenFile("foo", os.O_RDONLY, 0o644)
|
func f1(n int) float64 {
result := 10_000.
for i := 0; i < n; i++ {
result += 1.0001
}
return result
}
func f2(n int) float64 {
result := 0.
for i := 0; i < n; i++ {
result += 1.0001
}
return result + 10_000.
}
a := 100000.001
b := 1.0001
c := 1.0002
fmt.Println(a * (b + c))
fmt.Println(a*b + a*c)
200030.00200030004
200030.0020003
a := decimal.NewFromFloat(100000.001)
b := decimal.NewFromFloat(1.0001)
c := decimal.NewFromFloat(1.0002)
fmt.Println(a.Mul(b.Add(c))) //200030.0020003
s := make([]int, 3, 6)
panic: runtime error: index out of range [4] with length 3
s1 := make([]int, 3, 6)
s2 := s1[1:3]
s2 = append(s2, 3)
fmt.Println(s1) // [0 2 0]
fmt.Println(s2) // [2 0 3]
s1 = append(s1, 4)
fmt.Println(s1) // [0 2 0 4]
fmt.Println(s2) // [2 0 4]
s2 = append(s2, 5, 6, 7)
fmt.Println(s1) //[0 2 0 4]
fmt.Println(s2) //[2 0 4 5 6 7]
s1 := []int{1, 2, 3}
s2 := s1[1:2]
s3 := append(s2, 10)
s1=[1 2 10], s2=[2], s3=[2 10]
func main() {
var s []string
log(1, s)
s = []string(nil)
log(2, s)
s = []string{}
log(3, s)
s = make([]string, 0)
log(4, s)
}
func log(i int, s []string) {
fmt.Printf("%d: empty=%t\tnil=%t\n", i, len(s) == 0, s == nil)
}
1: empty=true nil=true
2: empty=true nil=true
3: empty=true nil=false
4: empty=true nil=false
func f() []string {
var s []string
if foo() {
s = append(s, "foo")
}
if bar() {
s = append(s, "bar")
}
return s
}
s := []string{"foo", "bar", "baz"}
src := []int{0, 1, 2}
dst := append([]int(nil), src...)
BenchmarkConvert_EmptySlice-4 22 49739882 ns/op
BenchmarkConvert_GivenCapacity-4 86 13438544 ns/op
BenchmarkConvert_GivenLength-4 91 12800411 ns/op
src := []int{0, 1, 2}
var dst []int
copy(dst, src)
fmt.Println(dst) // []
src := []int{0, 1, 2}
dst := make([]int, len(src))
copy(dst, src)
fmt.Println(dst) //[0 1 2]
src := []int{0, 1, 2}
dst := append([]int(nil), src...)
type Foo struct {
v []byte
}
func keepFirstTwoElementsOnly(foos []Foo) []Foo {
return foos[:2]
}
func main() {
foos := make([]Foo, 1_000)
printAlloc()
for i := 0; i < len(foos); i++ {
foos[i] = Foo{
v: make([]byte, 1024*1024),
}
}
printAlloc()
two := keepFirstTwoElementsOnly(foos)
runtime.GC()
printAlloc()
runtime.KeepAlive(two)
}
func printAlloc() {
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("%d KB\n", m.Alloc/1024)
}
387 KB
1024315 KB
1024319 KB
func keepFirstTwoElementsOnly(foos []Foo) []Foo {
res := make([]Foo, 2)
copy(res, foos)
return res
}
func keepFirstTwoElementsOnly(foos []Foo) []Foo {
for i := 2; i < len(foos); i++ {
foos[i].v = nil
}
return foos[:2]
}
type account struct {
balance float32
}
accounts := []account{
{balance: 100.},
{balance: 200.},
{balance: 300.},
}
for _, a := range accounts {
a.balance += 1000
}
[{100} {200} {300}]
for i := range accounts {
accounts[i].balance += 1000
}
s := []int{0, 1, 2}
for range s {
s = append(s, 10)
}
type Customer struct {
ID string
Balance float64
}
test := []Customer{
{ID: "1", Balance: 10},
{ID: "2", Balance: -10},
{ID: "3", Balance: 0},
}
var m map[string]*Customer
for _, customer := range test {
m[customer.ID] = &customer
}
{"1":{"ID":"3","Balance":0},"2":{"ID":"3","Balance":0},"3":{"ID":"3","Balance":0}}
for _, customer := range test {
fmt.Printf("%p\n", &customer) //我们想要获取这个指针的时候
}
0x1400000e240
0x1400000e240
0x1400000e240
for _, customer := range test {
current := customer // 使用局部变量
fmt.Printf("%p\n", ¤t) // 这里获取的指针是 range copy 出来元素的指针
}
for i := range test {
current := &test[i] // 使用局部变量
fmt.Printf("%p\n", current)
}
for i := 0; i < 5; i++ {
fmt.Printf("%d ", i)
switch i {
default:
case 2:
break
}
}
loop:
for i := 0; i < 5; i++ {
fmt.Printf("%d ", i)
switch i {
default:
case 2:
break loop
}
}
for {
select {
case <-ch:
// Do something
case <-ctx.Done():
break
}
}
loop:
for {
select {
case <-ch:
// Do something
case <-ctx.Done():
break loop
}
}
func readFiles(ch <-chan string) error {
for path := range ch {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
// Do something with file
}
return nil
}
func readFiles(ch <-chan string) error {
for path := range ch {
if err := readFile(path); err != nil {
return err
}
}
return nil
}
func readFile(path string) error {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
// Do something with file
return nil
}
func a() {
i := 0
defer notice(i) // 0
i++
return
}
func notice(i int) {
fmt.Println(i)
}
func a() {
i := 0
defer notice(&i) // 1
i++
return
}
func a() int {
i := 0
defer func() {
fmt.Println(i + 1) //12
}()
i++
return i+10
}
func TestA(t *testing.T) {
fmt.Println(a()) //11
}
先计算(i+10)-> (call defer) -> (return)
s := "hêllo"
for i := range s {
fmt.Printf("position %d: %c\n", i, s[i])
}
fmt.Printf("len=%d\n", len(s))
position 0: h
position 1: Ã
position 3: l
position 4: l
position 5: o
len=6
s := "hêllo"
for i, v := range s {
fmt.Printf("position %d: %c\n", i, v)
}
s := "hêllo"
runes := []rune(s)
for i, _ := range runes {
fmt.Printf("position %d: %c\n", i, runes[i])
}
position 0: h
position 1: ê
position 2: l
position 3: l
position 4: o
func (s store) handleLog(log string) error {
if len(log) < 36 {
return errors.New("log is not correctly formatted")
}
uuid := log[:36]
s.store(uuid)
// Do something
}
func (s store) handleLog(log string) error {
if len(log) < 36 {
return errors.New("log is not correctly formatted")
}
uuid := strings.Clone(log[:36]) // copy一份
s.store(uuid)
// Do something
}
type MultiError struct {
errs []string
}
func (m *MultiError) Add(err error) {
m.errs = append(m.errs, err.Error())
}
func (m *MultiError) Error() string {
return strings.Join(m.errs, ";")
}
func Validate(age int, name string) error {
var m *MultiError
if age < 0 {
m = &MultiError{}
m.Add(errors.New("age is negative"))
}
if name == "" {
if m == nil {
m = &MultiError{}
}
m.Add(errors.New("name is nil"))
}
return m
}
func Test(t *testing.T) {
if err := Validate(10, "a"); err != nil {
t.Errorf("invalid")
}
}
invalid <nil>
err:= xxx()
if err != nil {
return err
}
err:= xxx()
if err != nil {
return XXError{Err: err}
}
if err != nil {
return fmt.Errorf("xxx failed: %w", err)
}
if err != nil {
return fmt.Errorf("xxx failed: %v", err)
}
var BaseErr = errors.New("base error")
func main() {
err1 := fmt.Errorf("wrap base: %w", BaseErr)
err2 := fmt.Errorf("wrap err1: %w", err1)
println(err2 == BaseErr)
if !errors.Is(err2, BaseErr) {
panic("err2 is not BaseErr")
}
println("err2 is BaseErr")
}
false
err2 is BaseErr
type TypicalErr struct {
e string
}
func (t TypicalErr) Error() string {
return t.e
}
func main() {
err := TypicalErr{"typical error"}
err1 := fmt.Errorf("wrap err: %w", err)
err2 := fmt.Errorf("wrap err1: %w", err1)
var e TypicalErr
if !errors.As(err2, &e) {
panic("TypicalErr is not on the chain of err2")
}
println("TypicalErr is on the chain of err2")
println(err == e)
}
TypicalErr is on the chain of err2
true
func getBalance(db *sql.DB, clientID string) (
float32, error) {
rows, err := db.Query(query, clientID)
if err != nil {
return 0, err
}
defer rows.Close()
// Use rows
}
defer func() {
err := rows.Close()
if err != nil {
log.Printf("failed to close rows: %v", err)
}
return err //无法通过编译
}()
func getBalance(db *sql.DB, clientID string) (balance float32, err error) {
rows, err = db.Query(query, clientID)
if err != nil {
return 0, err
}
defer func() {
err = rows.Close()
}()
// Use rows
}
defer func() {
closeErr := rows.Close()
if err != nil {
if closeErr != nil {
log.Printf("failed to close rows: %v", err)
}
return
}
err = closeErr
}()
i := 0
go func() {
i++
}()
i := 0
go func() {
i++
}()
fmt.Println(i)
var c = make(chan int, 10)
var a string
func f() {
a = "hello, world"
c <- 0
}
func main() {
go f()
<-c
print(a)
}
variable change -》channel send -》channel receive -》variable read
i := 0
ch := make(chan struct{})
go func() {
<-ch
fmt.Println(i)
}()
i++
close(ch)
var c = make(chan int)
var a string
func f() {
a = "hello, world"
<-c
}
func main() {
go f()
c <- 0
print(a)
}
fmt.Println(ctx.Value("key"))
package provider
type key string
const myCustomKey key = "key"
func f(ctx context.Context) {
ctx = context.WithValue(ctx, myCustomKey, "foo")
// ...
}
ch := foo()
go func() {
for v := range ch {
// ...
}
}()
func main() {
newWatcher()
// Run the application
}
type watcher struct { /* Some resources */ }
func newWatcher() {
w := watcher{}
go w.watch()
}
func main() {
w := newWatcher()
defer w.close()
// Run the application
}
func newWatcher() watcher {
w := watcher{}
go w.watch()
return w
}
func (w watcher) close() {
// Close the resources
}
for {
select {
case v := <-messageCh:
fmt.Println(v)
case <-disconnectCh:
fmt.Println("disconnection, return")
return
}
}
for i := 0; i < 10; i++ {
messageCh <- i
}
disconnectCh <- struct{}{}
0
1
2
3
4
disconnection, return
|
for {
select {
case v := <-messageCh:
fmt.Println(v)
case <-disconnectCh:
for {
select {
case v := <-messageCh:
fmt.Println(v)
default:
fmt.Println("disconnection, return")
return
}
}
}
}
var ch chan int
ch <- 0 //block
var ch chan int
<-ch //block
ch1 := make(chan int, 1)
close(ch1)
for {
v := <-ch1
fmt.Println(v)
}
func merge(ch1, ch2 <-chan int) <-chan int {
ch := make(chan int, 1)
go func() {
for {
select {
case v:= <-ch1:
ch <- v
case v:= <-ch2:
ch <- v
}
}
close(ch) // 永远运行不到
}()
return ch
}
v, open := <-ch1
fmt.Print(v, open) //open返回false 表示没有被关闭
func merge(ch1, ch2 <-chan int) <-chan int {
ch := make(chan int, 1)
ch1Closed := false
ch2Closed := false
go func() {
for {
select {
case v, open := <-ch1:
if !open { // 如果已经关闭
ch1Closed = true //标记为true
break
}
ch <- v
case v, open := <-ch2:
if !open { // 如果已经关闭
ch2Closed = true//标记为true
break
}
ch <- v
}
if ch1Closed && ch2Closed {//都关闭了
close(ch)//关闭ch
return
}
}
}()
return ch
}
type Customer struct {
mutex sync.RWMutex
id string
age int
}
func (c *Customer) UpdateAge(age int) error {
c.mutex.Lock()
defer c.mutex.Unlock()
if age < 0 {
return fmt.Errorf("age should be positive for customer %v", c)
}
c.age = age
return nil
}
func (c *Customer) String() string {
fmt.Println("enter string method")
c.mutex.RLock()
defer c.mutex.RUnlock()
return fmt.Sprintf("id %s, age %d", c.id, c.age)
}
mutex.Lock -> check age -> Format error -> call String() -> mutex.RLock
wg := sync.WaitGroup{}
var v uint64
for i := 0; i < 3; i++ {
go func() {
wg.Add(1)
atomic.AddUint64(&v, 1)
wg.Done()
}()
}
wg.Wait()
fmt.Println(v)
type Counter struct {
mu sync.Mutex
counters map[string]int
}
func (c Counter) Increment(name string) {
c.mu.Lock()
defer c.mu.Unlock()
c.counters[name]++
}
func NewCounter() Counter {
return Counter{counters: map[string]int{}}
}
func main() {
counter := NewCounter()
go counter.Increment("aa")
go counter.Increment("bb")
}
|
» go vet . bear@BEARLUO-MB7
# github.com/cch123/gogctuner/main
./main.go:53:9: Increment passes lock by value: github.com/cch123/gogctuner/main.Counter contains sync.Mutex
package main
import (
"fmt"
"time"
)
//define a channel
var chs chan int
func Get() {
for {
select {
case v := <- chs:
fmt.Printf("print:%v\n", v)
case <- time.After(3 * time.Minute):
fmt.Printf("time.After:%v", time.Now().Unix())
}
}
}
func Put() {
var i = 0
for {
i++
chs <- i
}
}
func main() {
chs = make(chan int, 100)
go Put()
Get()
}
$ go tool pprof -http=:8081 http://localhost:6060/debug/pprof/heap
func Get() {
delay := time.NewTimer(3 * time.Minute)
defer delay.Stop()
for {
delay.Reset(3 * time.Minute)
select {
case v := <- chs:
fmt.Printf("print:%v\n", v)
case <- delay.C:
fmt.Printf("time.After:%v", time.Now().Unix())
}
}
}
type handler struct {
client http.Client
url string
}
func (h handler) getBody() (string, error) {
resp, err := h.client.Get(h.url)
if err != nil {
return "", err
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
return string(body), nil
}
defer func() {
err := resp.Body.Close()
if err != nil {
log.Printf("failed to close response: %v\n", err)
}
}()
cat /sys/devices/system/cpu/cpu1/cache/index0/coherency_line_size
64
func sum2(s []int64) int64 {
var total int64
for i := 0; i < len(s); i += 2 {
total += s[i]
}
return total
}
func sum8(s []int64) int64 {
var total int64
for i := 0; i < len(s); i += 8 {
total += s[i]
}
return total
}
type Foo struct {
a int64
b int64
}
func sumFoo(foos []Foo) int64 {
var total int64
for i := 0; i < len(foos); i++ {
total += foos[i].a
}
return total
}
type Bar struct {
a []int64
b []int64
}
func sumBar(bar Bar) int64 {
var total int64
for i := 0; i < len(bar.a); i++ {
total += bar.a[i]
}
return total
}
func Benchmark_sumBar(b *testing.B) {
s := Bar{
a: make([]int64, 16),
b: make([]int64, 16),
}
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
sumBar(s)
}
})
}
func Benchmark_sumFoo(b *testing.B) {
s := make([]Foo, 16)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
sumFoo(s)
}
})
}
# go test -gcflags "-N -l" -bench .
Benchmark_sumBar-16 249029368 4.855 ns/op
Benchmark_sumFoo-16 238571205 5.056 ns/op
type MyAtomic interface {
IncreaseAllEles()
}
type Pad struct {
a uint64
_p1 [15]uint64
b uint64
_p2 [15]uint64
c uint64
_p3 [15]uint64
}
func (myatomic *Pad) IncreaseAllEles() {
atomic.AddUint64(&myatomic.a, 1)
atomic.AddUint64(&myatomic.b, 1)
atomic.AddUint64(&myatomic.c, 1)
}
type NoPad struct {
a uint64
b uint64
c uint64
}
func (myatomic *NoPad) IncreaseAllEles() {
atomic.AddUint64(&myatomic.a, 1)
atomic.AddUint64(&myatomic.b, 1)
atomic.AddUint64(&myatomic.c, 1)
}
func testAtomicIncrease(myatomic MyAtomic) {
paraNum := 1000
addTimes := 1000
var wg sync.WaitGroup
wg.Add(paraNum)
for i := 0; i < paraNum; i++ {
go func() {
for j := 0; j < addTimes; j++ {
myatomic.IncreaseAllEles()
}
wg.Done()
}()
}
wg.Wait()
}
func BenchmarkNoPad(b *testing.B) {
myatomic := &NoPad{}
b.ResetTimer()
testAtomicIncrease(myatomic)
}
func BenchmarkPad(b *testing.B) {
myatomic := &Pad{}
b.ResetTimer()
testAtomicIncrease(myatomic)
}
BenchmarkNoPad
BenchmarkNoPad-10 1000000000 0.1360 ns/op
BenchmarkPad
BenchmarkPad-10 1000000000 0.08887 ns/op
type M struct {
m int64
x struct{}
}
type N struct {
x struct{}
n int64
}
func main() {
m := M{}
n := N{}
fmt.Printf("as final field size:%d\nnot as final field size:%d\n", unsafe.Sizeof(m), unsafe.Sizeof(n))
}
as final field size:16
not as final field size:8
$ go install golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment@latest
$ fieldalignment -fix .\main\my.go
main\my.go:13:9: struct of size 24 could be 16
go run -gcflags '-m -l' main.go
|
type Demo struct {
name string
}
func createDemo(name string) *Demo {
d := new(Demo) // 局部变量 d 逃逸到堆
d.name = name
return d
}
func main() {
demo := createDemo("demo")
fmt.Println(demo)
}
go run -gcflags '-m -l' .\main\main.go
# command-line-arguments
main\main.go:12:17: leaking param: name
main\main.go:13:10: new(Demo) escapes to heap
main\main.go:20:13: ... argument does not escape
&{demo}
func createDemo(name string) any {
d := new(Demo) // 局部变量 d 逃逸到堆
d.name = name
return d
}
func main() {
number := 10
s1 := make([]int, 0, number)
for i := 0; i < number; i++ {
s1 = append(s1, i)
}
s2 := make([]int, 0, 10)
for i := 0; i < 10; i++ {
s2 = append(s2, i)
}
}
go run -gcflags '-m -l' main.go
./main.go:65:12: make([]int, 0, number) escapes to heap
./main.go:69:12: make([]int, 0, 10) does not escape
func Increase() func() int {
n := 0
return func() int {
n++
return n
}
}
func main() {
in := Increase()
fmt.Println(in()) // 1
fmt.Println(in()) // 2
}
go run -gcflags '-m -l' main.go
./main.go:64:5: moved to heap: n
./main.go:65:12: func literal escapes to heap
// toBytes performs unholy acts to avoid allocations
func toBytes(s string) []byte {
return *(*[]byte)(unsafe.Pointer(&s))
}
// toString performs unholy acts to avoid allocations
func toString(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}
import _ "go.uber.org/automaxprocs"
func main() {
// Your application logic here
}
参考:
https://go.dev/ref/mem
https://colobu.com/2019/01/24/cacheline-affects-performance-in-go/
https://teivah.medium.com/go-and-cpu-caches-af5d32cc5592
https://geektutu.com/post/hpg-escape-analysis.html
https://github.com/uber-go/automaxprocs
https://gfw.go101.org/article/unsafe.html
使用Go语言时还有什么易错点?欢迎在评论区分享。我们将选取1则最有意义的分享,送出腾讯云开发者-文化衫1件(见下图)。6月12日中午12点开奖。
关注星标腾讯云开发者
第一时间看鹅厂技术干货