func safeDivide(a, b int) (result int, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("recovered from panic: %v", r)
        }
    }()
    result = a / b
    return
}
将panic 捕获转化为 error
package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	// 创建一个可取消的context
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()  // 确保函数结束时取消context

	// 启动一个goroutine,监听cancel信号
	go func(ctx context.Context) {
                // 判断是否有设置Deadline,如果没有则为设置了cancel函数
                if deadline, ok := ctx.Deadline(); ok {
                    fmt.Println("Deadline set for:", deadline)
                } else {
                    fmt.Println("No deadline set.")
                }
		for {
			select {
			case <-ctx.Done():
				fmt.Println("Goroutine被取消")
				return
			default:
				fmt.Println("Goroutine运行中")
				time.Sleep(1 * time.Second)
			}
		}
	}(ctx)

	// 运行3秒后取消Context
	time.Sleep(3 * time.Second)
	cancel()

	// 等待一会儿以便观察输出
	time.Sleep(1 * time.Second)
	fmt.Println("主程序结束")
}

context cannel 使用
package main

import (
	"context"
	"fmt"
	"time"
)

// worker 模拟一个工作函数,它会从 context 中获取数据
func worker(ctx context.Context) {
	// 从 context 中获取 userID 数据
	userID, ok := ctx.Value("userID").(int)
	if ok {
		fmt.Printf("Worker received userID: %d\n", userID)
	} else {
		fmt.Println("Worker did not receive valid userID")
	}

	// 从 context 中获取 name 数据
	name, ok := ctx.Value("name").(string)
	if ok {
		fmt.Printf("Worker received name: %s\n", name)
	} else {
		fmt.Println("Worker did not receive valid name")
	}

	for {
		select {
		case <-ctx.Done():
			fmt.Println("Worker received cancel signal, stopping...")
			return
		default:
			fmt.Println("Worker is working...")
			time.Sleep(1 * time.Second)
		}
	}
}

func main() {
	// 创建一个可取消的 context,并使用 WithValue 传递多组数据
	ctx := context.WithValue(context.Background(), "userID", 123)
	ctx = context.WithValue(ctx, "name", "qiulaoshi")
	ctx, cancel := context.WithCancel(ctx)

	// 启动一个 goroutine 来执行工作
	go worker(ctx)

	// 模拟一段时间后取消工作
	time.Sleep(5 * time.Second)
	cancel()

	// 等待一段时间,确保 worker 有足够的时间停止
	time.Sleep(2 * time.Second)
	fmt.Println("Main function exiting...")
}

反射修改数据

package main
import (
    "fmt"
    "reflect"
)
func main() {
    n := 1.2345
    fmt.Println("old value :", n)
    // 通过reflect.ValueOf获取n的地址的reflect.Value对象,注意这里必须取指针
    v := reflect.ValueOf(&n)
    // 通过Elem方法获取n的值对应的reflect.Value对象(因为n是一个非指针变量,所以需要先获取其地址)
    e := v.Elem()
    fmt.Println("type :", e.Type())
    fmt.Println("can set :", e.CanSet()) // 应该返回true,因为n是一个可导出的变量且可以通过指针访问
    // 修改变量的值
    e.SetFloat(2.123)
    fmt.Println("new value :", n) // 输出: new value : 2.123
}

数据比较使用 reflect.DeepEqual
package main

import (
	"fmt"
	"reflect"
)

func main() {
	m1 := map[string]int{"a": 1, "b": 2, "c": 3}
	m2 := map[string]int{"a": 1, "c": 3, "b": 2}
	fmt.Println(reflect.DeepEqual(m1, m2)) // true

	m3 := map[string][]int{"a": []int{1, 2, 3}}
	m4 := map[string][]int{"a": []int{1, 3, 2}}
	fmt.Println(reflect.DeepEqual(m3, m4)) // false
}

修改私有变量的值
package main

import (
    "fmt"
    "reflect"
)

type Person struct {
    name string // 私有字段
    Age  int    // 公有字段
}

func main() {
    p := &Person{name: "Alice", Age: 20}
    // 获取结构体指针的反射值
    value := reflect.ValueOf(p)
    // 解引用指针
    elem := value.Elem()
    // 尝试获取私有字段
    field := elem.FieldByName("name")
    if field.IsValid() {
        // 检查是否可以设置值
        if field.CanSet() {
            field.SetString("Bob")
        } else {
            fmt.Println("Cannot set private field")
        }
    }
    fmt.Println(p.name) // 输出: Bob
}

// person.go
package person

type Person struct {
    name string // 私有字段
    Age  int    // 公有字段
}

// main.go
package main

import (
    "fmt"
    "reflect"
    "./person"
)

func main() {
    p := &person.Person{name: "Alice", Age: 20}
    value := reflect.ValueOf(p)
    elem := value.Elem()
    field := elem.FieldByName("name")
    if field.IsValid() {
        if field.CanSet() {
            field.SetString("Bob")
        } else {
            fmt.Println("Cannot set private field")
        }
    }
    fmt.Println(p.Age) // 可以访问公有字段
    // 无法访问 p.name,因为它是私有字段
}

package main

import (
  "fmt"
  "unsafe"
)

type MyStruct struct {
  PublicField  int
  privateField int // 私有成员
}

func main() {
  s := MyStruct{PublicField: 5, privateField: 10}
  fmt.Println("Before:", s)
  // 注意如果这里获取的刚好是结构体的第一个字段的话,这里不需要使用offset了。
  privateFieldOffset := unsafe.Offsetof(s.privateField)

  // 获取结构体的地址并转换为指针
  pointer := unsafe.Pointer(&s)

  // 根据偏移量计算私有成员的地址
  privateFieldPointer := (*int)(unsafe.Pointer(uintptr(pointer) + privateFieldOffset))

  // 修改私有成员的值
  *privateFieldPointer = 20

  fmt.Println("After:", s)
}

类型转换:
  • 适用于基本数据类型之间的转换,如 int、float64、string 等。

  • 类型转换语法为:T(v),其中 T 是目标类型,v 是待转换的值。

类型断言:
  • 适用于接口类型,用于从接口值中提取具体的动态类型。

  • 类型断言语法为:v.(T),其中 T 是目标类型,v 是接口变量

多态代码
package main

import "fmt"

// 定义一个接口
type Animal interface {
    Speak() string
}

// Dog 类型实现了 Animal 接口
type Dog struct{}

func (d Dog) Speak() string {
    return "Woof!"
}

// Cat 类型实现了 Animal 接口
type Cat struct{}

func (c Cat) Speak() string {
    return "Meow!"
}

// Person 类型实现了 Animal 接口
type Person struct {
    Name string
}

func (p Person) Speak() string {
    return "Hello!"
}

// 多态函数:接收 Animal 接口
func MakeSound(a Animal) {
    fmt.Println(a.Speak())
}

func main() {
    animals := []Animal{Dog{}, Cat{}, Person{Name: "John"}}

    // 遍历不同类型,调用接口方法
    for _, animal := range animals {
        MakeSound(animal)
    }
}

并发相关代码

sync.WaitGroup是一个用于并发控制的同步原语,主要用于等待一组协程完成。它的实现原理基于计数器机制,可以利用三个简单的方法实现协程同步:Add、Done 和 wait。

1) Add(delta int):增加或减少计数器的值,delta 可以为正数或负数。

2) Done():減少计数器的值,相当于 Add(-1)。

3) wait():阻塞调用方的协程,直到计数器的值变为零。

底层本质上是使用 CAS + state 原子操作 + 信号量 来实现的。

  • state 是一个 64 位的原子变量,高 32 位保存计数器,低32 位保存当前等待的 goroutine 数量。

  • 信号量用于管理阻塞和唤醒 goroutine。如果计数器减为 0且等待数大于 0则会通过信号量唤醒等待的 goroutine

读写锁(sync.RWMutex)在底层是通过互斥锁(sync.Mutex)和若干个计数器来实现的。读写锁允许多个读操作并发进行,但写操作是独占的。

1)写锁:

写锁通过互斥锁(sync.Mutex)来实现。写操作请求到来时,首先会尝试获取写锁(wLock)。一旦成功获取,写操作会独占资源,直到释放写锁。

在写锁持有期间,所有读锁请求都会被阻塞,新的读操作需要等待写锁释放。写锁获取时,会更新 readerCount ,将其设置负值,以通知当前的读锁请求存在写锁竞争。当有大量读锁请求时,写锁会先挂起等待所有的读锁释放才得以获取。

2)读锁:

读锁通过原子计数器 readerCount 来管理并发读操作的数量。每当一个读操作请求到来时,readerCount 会通过原子操作增加1;当读锁释放时,readerCount 会减少1。读锁请求会首先检查 readerCount,如果它为负数(表示有写操作正在进行或等待),则新来的读操作会被挂起,进入一个等待队列(readers)。只有在没有写锁请求时,多个读锁才能并发执行。当所有读锁完成后,如果有写锁请求,写锁会被唤醒,开始执行。

package main

import (
    "fmt"
    "sync"
    "time"
)

// 定义一个共享数据结构
type SharedData struct {
    data  int
    mutex sync.RWMutex
}

// 读取共享数据的函数
func (sd *SharedData) ReadData() int {
    // 获取读锁
    sd.mutex.RLock()
    // 在函数结束时释放读锁
    defer sd.mutex.RUnlock()
    return sd.data
}

// 写入共享数据的函数
func (sd *SharedData) WriteData(newData int) {
    // 获取写锁
    sd.mutex.Lock()
    // 在函数结束时释放写锁
    defer sd.mutex.Unlock()
    sd.data = newData
}

func main() {
    sharedData := SharedData{data: 0}
    var wg sync.WaitGroup

    // 启动多个读 goroutine
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            for j := 0; j < 3; j++ {
                // 模拟读取操作
                result := sharedData.ReadData()
                fmt.Printf("Reader %d read data: %d\n", id, result)
                time.Sleep(100 * time.Millisecond)
            }
        }(i)
    }

    // 启动一个写 goroutine
    wg.Add(1)
    go func() {
        defer wg.Done()
        for k := 0; k < 3; k++ {
            // 模拟写入操作
            newData := k + 1
            sharedData.WriteData(newData)
            fmt.Printf("Writer wrote data: %d\n", newData)
            time.Sleep(200 * time.Millisecond)
        }
    }()

    // 等待所有 goroutine 完成
    wg.Wait()
    fmt.Println("All goroutines have finished.")
}
    

channel 数据结构
type hchan struct {
	qcount   uint           // 队列中所有数据总数
	dataqsiz uint           // 环形队列的 size
	buf      unsafe.Pointer // 指向 dataqsiz 长度的数组
	elemsize uint16         // 单个元素大小
	closed   uint32         // 队列是否已经被关闭
	elemtype *_type // 保存的元素类型
	sendx    uint   // 已发送的元素在环形队列中的位置
	recvx    uint   // 已接收的元素在环形队列中的位置
	recvq    waitq  // 接收者的等待队列
	sendq    waitq  // 发送者的等待队列

	lock mutex  // 数据保护锁
}

什么是闭包

闭包的应用场景

•状态保持:闭包可以用于在函数调用之间保持状态,例如实现计数器、累加器等。

•函数工厂:根据不同的参数生成不同功能的函数。

•回调函数:在异步操作或事件处理中,闭包可以携带上下文信息

func main() {
    x := 10
    f := func(y int) int {
        return x + y
    }
    fmt.Println(f(5))
}

  1. 如果原 slice 容量小于1024个元素,新的容量会是原容量的 2倍。

  2. 如果原 slice 容量大于等于1024个元素,新的容量会增加原容量的1/4。

unsafe.Pointer 与 uintptr
  1. unsafe.Pointer:是一个通用指针类型,可以与任意类型的指针相互转换。它主要用于在不同类型的指针之间进行转换,但不能直接参与指针运算。

  2. uintptr:是一个能够存储指针的无符号整数类型。用于指针的数值表示,可进行指针运算,如地址偏移等。但uintptr 仅仅是数值,不被垃圾回收器视为指针,所以如果将指针转换为 uintptr,可能会导致垃圾回收器回收其引用的对象。

var i int = 42
var p *int = &i
var fp *float64 = (*float64)(unsafe.Pointer(p))


import "unsafe"

type S struct {
  a int
  b float64
}

var s S
bPtr := (*float64)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.b)))

限流策略
package main

import (
    "fmt"
    "log"
    "net/http"
    "time"
    
    "golang.org/x/time/rate"
)

const (
    qps        = 500 // 每秒请求数
    burstLimit = 10  // 突发请求限额
)

func main() {
    limiter := rate.NewLimiter(rate.Limit(qps), burstLimit)
    
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        if limiter.Allow() {
            // 自己的接口逻辑
            fmt.Fprintf(w, "Hello, world!") // 示例:把Hello, world!这个字符串写到response里
        } else {
            http.Error(w, "Too many requests", http.StatusTooManyRequests) // 限流500QPS
        }
    })

    log.Printf("Starting server on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

线程池

package main

import (
    "fmt"
    "sync"
)

func worker(id int, wg *sync.WaitGroup, jobs <-chan int, results chan<- int) {
    defer wg.Done()
    for j := range jobs {
        results <- j * 2
    }
}

func main() {
    const numJobs = 10
    jobs := make(chan int, numJobs)
    results := make(chan int, numJobs)

    var wg sync.WaitGroup
    for w := 1; w <= 3; w++ {
        wg.Add(1)
        go worker(w, &wg, jobs, results)
    }

    for j := 1; j <= numJobs; j++ {
        jobs <- j
    }
    close(jobs)

    wg.Wait()
    close(results)

    for result := range results {
        fmt.Println(result)
    }
}

trace 分析 go tool trace trace.out
package main

import (
	"os"
	"runtime/trace"
	"time"
)

func task() {
	for i := 0; i < 3; i++ {
		time.Sleep(1 * time.Second)
	}
}

func main() {
	f, err := os.Create("trace.out")
	if err != nil {
		panic(err)
	}
	defer f.Close()

	err = trace.Start(f)
	if err != nil {
		panic(err)
	}
	defer trace.Stop()

	go task()
	time.Sleep(5 * time.Second)
}

服务端客户端通信
package main

import (
    "bufio"
    "fmt"
    "log"
    "net"
    "os"
)

func main() {
    // 连接到服务器
    conn, err := net.Dial("tcp", "localhost:8080")
    if err != nil {
        log.Fatal("Error connecting:", err)
    }
    defer conn.Close()
    fmt.Println("Connected to server")
    reader := bufio.NewReader(os.Stdin)
    for {
        fmt.Print("Enter message to send: ")
        message, err := reader.ReadString('\n')
        if err != nil {
            log.Println("Error reading input:", err)
            break
        }
        // 发送消息给服务器
        _, err = conn.Write([]byte(message))
        if err != nil {
            log.Println("Error sending message:", err)
            break
        }
        // 接收服务器的响应
        response, err := bufio.NewReader(conn).ReadString('\n')
        if err != nil {
            log.Println("Error receiving response:", err)
            break
        }
        fmt.Print("Server response: ", response)
    }
}
    

package main

import (
    "bufio"
    "fmt"
    "log"
    "net"
)

func handleConnection(conn net.Conn) {
    defer conn.Close()
    reader := bufio.NewReader(conn)
    for {
        message, err := reader.ReadString('\n')
        if err != nil {
            log.Println("Error reading:", err)
            return
        }
        fmt.Print("Received message: ", message)
        // 回显消息给客户端
        _, err = conn.Write([]byte("Server received: " + message))
        if err != nil {
            log.Println("Error writing:", err)
            return
        }
    }
}

func main() {
    // 监听本地 8080 端口
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        log.Fatal("Error listening:", err)
    }
    defer listener.Close()
    fmt.Println("Server is listening on port 8080")
    for {
        // 接受客户端连接
        conn, err := listener.Accept()
        if err != nil {
            log.Println("Error accepting connection:", err)
            continue
        }
        // 为每个客户端连接启动一个新的 goroutine 处理
        go handleConnection(conn)
    }
}
    

GMP模型

sync.Pool
package main

import (
	"fmt"
	"sync"
)

// 创建一个 sync.Pool 实例
var byteSlicePool = sync.Pool{
	// 当池中没有可用对象时,New 函数会被调用以创建新对象
	New: func() interface{} {
		// 创建一个长度为 1024 的字节切片
		return make([]byte, 1024)
	},
}

func main() {
	// 从池中获取一个对象
	byteSlice := byteSlicePool.Get().([]byte)
	fmt.Printf("Got slice from pool, len: %d, cap: %d\n", len(byteSlice), cap(byteSlice))

	// 使用这个切片
	for i := 0; i < 10; i++ {
		byteSlice[i] = 'A'
	}
	fmt.Printf("Used slice: %s\n", string(byteSlice[:10]))

	// 使用完毕后,将对象放回池中以便复用
	byteSlicePool.Put(byteSlice)
	fmt.Println("Put slice back to pool")

	// 再次从池中获取对象
	newByteSlice := byteSlicePool.Get().([]byte)
	fmt.Printf("Got another slice from pool, len: %d, cap: %d\n", len(newByteSlice), cap(newByteSlice))
}
    

CAS基础使用
C

sync.Once 单例使用
package main

import (
    "fmt"
    "sync"
)

type Singleton struct{}

var (
    instance *Singleton
    once     sync.Once
)

func GetInstance() *Singleton {
    once.Do(func() {
        instance = &Singleton{}
        fmt.Println("Singleton instance created")
    })
    return instance
}

func main() {
    for i := 0; i < 10; i++ {
        go func() {
            _ = GetInstance()
        }()
    }
    // 为了确保所有 Goroutine 执行完毕
    var input string
    fmt.Scanln(&input)
}

sync.WaitGroup
package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {
	// 创建一个 WaitGroup 用于等待所有 goroutine 完成
	var wg sync.WaitGroup

	// 启动 5 个 goroutine
	for i := 1; i <= 5; i++ {
		// 每启动一个 goroutine 调用 Add(1) 表示增加一个待等待的任务
		wg.Add(1)

		go func(i int) {
			// 确保任务完成时调用 Done() 来减少 WaitGroup 的计数
			defer wg.Done()

			// 模拟每个 goroutine 执行一些任务
			fmt.Printf("Goroutine %d is starting...\n", i)
			time.Sleep(time.Second * time.Duration(i)) // 模拟不同的执行时间
			fmt.Printf("Goroutine %d has finished.\n", i)
		}(i)
	}

	// 等待所有 goroutine 完成
	wg.Wait()

	// 所有 goroutine 完成后打印
	fmt.Println("All goroutines have finished.")
}

sync.WaitGroup

package main

import (
    "fmt"
    "sync"
    "time"
)

var (
    mu      sync.Mutex
    cond    = sync.NewCond(&mu)
    buffer  = make([]int, 0, 10)
)

func produce(item int) {
    mu.Lock()
    for len(buffer) == cap(buffer) {
        cond.Wait() // 缓冲区已满,等待消费者消费
    }
    buffer = append(buffer, item)
    fmt.Printf("Produced: %d\n", item)
    cond.Signal() // 唤醒等待的消费者
    mu.Unlock()
}

func consume() {
    mu.Lock()
    for len(buffer) == 0 {
        cond.Wait() // 缓冲区为空,等待生产者生产
    }
    item := buffer[0]
    buffer = buffer[1:]
    fmt.Printf("Consumed: %d\n", item)
    cond.Signal() // 唤醒等待的生产者
    mu.Unlock()
}

func main() {
    var wg sync.WaitGroup
    wg.Add(2)

    go func() {
        defer wg.Done()
        for i := 0; i < 10; i++ {
            produce(i)
            time.Sleep(100 * time.Millisecond)
        }
    }()

    go func() {
        defer wg.Done()
        for i := 0; i < 10; i++ {
            consume()
            time.Sleep(150 * time.Millisecond)
        }
    }()

    wg.Wait()
    fmt.Println("所有操作已完成")
}

sync.RWMutex
package main

import (
    "fmt"
    "sync"
    "time"
)

var (
    rwMutex sync.RWMutex
    data    int
    wg      sync.WaitGroup
)

func readData(id int) {
    defer wg.Done()
    rwMutex.RLock() // 获取读锁
    fmt.Printf("Goroutine %d: 读取数据 %d\n", id, data)
    time.Sleep(1 * time.Second) // 模拟读取操作耗时
    rwMutex.RUnlock() // 释放读锁
}

func writeData(id, value int) {
    defer wg.Done()
    rwMutex.Lock() // 获取写锁
    fmt.Printf("Goroutine %d: 写入数据 %d\n", id, value)
    data = value
    time.Sleep(2 * time.Second) // 模拟写入操作耗时
    rwMutex.Unlock() // 释放写锁
}

func main() {
    data = 0
    wg.Add(5)

    // 启动3个读取操作的 Goroutine
    go readData(1)
    go readData(2)
    go readData(3)

    // 启动2个写入操作的 Goroutine
    go writeData(4, 10)
    go writeData(5, 20)

    wg.Wait()
    fmt.Println("所有操作已完成")
}