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))
}
如果原 slice 容量小于1024个元素,新的容量会是原容量的 2倍。
如果原 slice 容量大于等于1024个元素,新的容量会增加原容量的1/4。
unsafe.Pointer 与 uintptr
unsafe.Pointer:是一个通用指针类型,可以与任意类型的指针相互转换。它主要用于在不同类型的指针之间进行转换,但不能直接参与指针运算。
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("所有操作已完成")
}