Go语言的“避坑”与技巧

本文探讨了Go语言中合理使用并发特性,如理解goroutine生命周期和避免不必要的通道使用,以及在实际案例中如何优雅处理。此外,还分析了反射的双刃剑特性,通过基准测试展示了反射在性能上的影响。最后,提到了接口的nil判断、多键索引查询以及TCP粘包的处理策略,提供了相关代码示例。
摘要由CSDN通过智能技术生成

一、合理地使用并发特性

1、了解goroutine的生命期时再创建goroutine

代码如下:

package main

import (
	"fmt"
	"runtime"
)

//一段耗时的计算函数
func consumer(ch chan int) {
	//无限获取数据的循环
	for {
		//从通道获取数据
		data := <-ch
		//打印数据
		fmt.Println(data)
	}
}

func main() {
	//创建一个传递数据用的通道
	ch := make(chan int)
	for {
		//空变量,什么也不做
		var dummy string
		//获取输入,模拟进程持续运行
		fmt.Scan(&dummy)
		//启动并发执行consumer()函数
		go consumer(ch)
		//输出现在的goroutine数量
		fmt.Println("goroutines:", runtime.NumGoroutine())
	}
}

运行结果如下:死循环

代码如下:

package main

import (
	"fmt"
	"runtime"
)

//一段耗时的计算函数
func consumer(ch chan int) {
	//无限获取数据的循环
	for {
		//从通道获取数据
		data := <-ch
		if data == 0 {
			break
		}
		//打印数据
		fmt.Println(data)
	}
}

func main() {
	//创建一个传递数据用的通道
	ch := make(chan int)
	for {
		//空变量,什么也不做
		var dummy string
		//获取输入,模拟进程持续运行
		fmt.Scan(&dummy)
		if dummy == "quit" {
			for i := 0; i < runtime.NumGoroutine()-1; i++ {
				ch <- 0
			}
			continue
		}
		//启动并发执行consumer()函数
		go consumer(ch)
		//输出现在的goroutine数量
		fmt.Println("goroutines:", runtime.NumGoroutine())
	}
}

运行结果如下:死循环

2、避免在不必要的地方使用通道

代码如下:

package main

import (
	"fmt"
	"net"
	"time"
)

//套接字接收过程
func socketRecv(conn net.Conn, exitChan chan string) {
	//创建一个接收的缓冲
	buff := make([]byte, 1024)
	//不停地接收数据
	for {
		//从套接字中读取数据
		_, err := conn.Read(buff)
		//需要结束接收,退出循环
		if err != nil {
			break
		}
	}
	//函数已经结束,发送通知
	exitChan <- "recv exit"
}

func main() {
	//连接一个地址
	conn, err := net.Dial("tcp", "www.163.com:80")
	//发生错误时打印错误退出
	if err != nil {
		fmt.Println(err)
		return
	}
	//创建退出通道
	exit := make(chan string)
	//并发执行套接字接收
	go socketRecv(conn, exit)
	//在接收时,等待1秒
	time.Sleep(time.Second)
	//主动关闭套接字
	conn.Close()
	//等待goroutine退出完毕
	fmt.Println(<-exit)
}

运行结果如下:

recv exit

代码如下:

package main

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

//套接字接收过程
func socketRecv(conn net.Conn, wg *sync.WaitGroup) {
	//创建一个接收的缓冲
	buff := make([]byte, 1024)
	//不停地接收数据
	for {
		//从套接字中读取数据
		_, err := conn.Read(buff)
		//需要结束接收,退出循环
		if err != nil {
			break
		}
	}
	//函数已经结束,发送通知
	wg.Done()
}

func main() {
	//连接一个地址
	conn, err := net.Dial("tcp", "www.163.com:80")
	//发生错误时打印错误退出
	if err != nil {
		fmt.Println(err)
		return
	}
	//退出通道
	var wg sync.WaitGroup
	//添加一个任务
	wg.Add(1)
	//并发执行接收套接字
	go socketRecv(conn, &wg)
	//在接收时,等待1秒
	time.Sleep(time.Second)
	//主动关闭套接字
	conn.Close()
	//等待goroutine退出完毕
	wg.Wait()
	fmt.Println("recv done")
}

运行结果如下:

recv done

二、反射:性能和灵活性的双刃剑

创建一个文件夹reflecttest,创建一个文件reflect_test.go。

代码如下:

package main

import (
	"reflect"
	"testing"
)

// 声明一个结构体,拥有1个字段
type data struct {
	Hp int
}

func BenchmarkNativeAssign(b *testing.B) {

	// 实例化结构体
	v := data{Hp: 2}

	// 停止基准测试的计时器
	b.StopTimer()
	// 重置基准测试计时器数据
	b.ResetTimer()

	// 重新启动基准测试计时器
	b.StartTimer()

	// 根据基准测试数据进行循环测试
	for i := 0; i < b.N; i++ {

		// 结构体成员赋值测试
		v.Hp = 3
	}

}

func BenchmarkReflectAssign(b *testing.B) {

	v := data{Hp: 2}

	// 取出结构体指针的反射值对象,并取其元素
	vv := reflect.ValueOf(&v).Elem()

	// 根据名字取结构体成员
	f := vv.FieldByName("Hp")

	b.StopTimer()
	b.ResetTimer()
	b.StartTimer()

	for i := 0; i < b.N; i++ {

		// 反射测试设置成员值性能
		f.SetInt(3)
	}
}

func BenchmarkReflectFindFieldAndAssign(b *testing.B) {

	v := data{Hp: 2}

	vv := reflect.ValueOf(&v).Elem()

	b.StopTimer()
	b.ResetTimer()
	b.StartTimer()

	for i := 0; i < b.N; i++ {

		// 测试结构体成员的查找和设置成员的性能
		vv.FieldByName("Hp").SetInt(3)
	}

}

func foo(v int) {

}

func BenchmarkNativeCall(b *testing.B) {

	for i := 0; i < b.N; i++ {

		// 原生函数调用
		foo(0)
	}
}

func BenchmarkReflectCall(b *testing.B) {

	// 取函数的反射值对象
	v := reflect.ValueOf(foo)

	b.StopTimer()
	b.ResetTimer()
	b.StartTimer()

	for i := 0; i < b.N; i++ {
		// 反射调用函数
		v.Call([]reflect.Value{reflect.ValueOf(2)})
	}

}

运行结果如下:

PS C:\Users\a-xiaobodou\OneDrive - Microsoft\Projects\Go\reflecttest> go test -v -bench=.
testing: warning: no tests to run
PASS
ok      _/C_/Users/a-xiaobodou/OneDrive_-_Microsoft/Projects/Go/reflecttest     0.326s [no tests to run]
PS C:\Users\a-xiaobodou\OneDrive - Microsoft\Projects\Go\reflecttest>

三、接口的nil判断

四、map的多键索引——多个数值条件可以同时查询

五、优雅地处理TCP粘包

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值