Go语言入门记录:从channel的池应用、sync的Pool、benchmark、反射reflect、json处理、http、性能分析和一些编程习惯

这篇具有很好参考价值的文章主要介绍了Go语言入门记录:从channel的池应用、sync的Pool、benchmark、反射reflect、json处理、http、性能分析和一些编程习惯。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  1. channel的一对一会阻塞,添加buffer不会阻塞。
func GetResponse() string {
	// 如果是这一句,确实只返回了1个,但是其他几个都阻塞了,浪费协程,浪费服务器资源,容易造成泄露等安全问题
	// ch := make(chan string)
	// 如果用下面这句,每个协程都只管往channel中传数据,传完就结束
	ch := make(chan string, 10)
	for i := 0; i < 10; i++ {
		go func(idx int) {
			time.Sleep(time.Millisecond * 10)
			ch <- "response from " + strconv.Itoa(idx)
		}(i)
	}
	return <-ch
	// 如果要返回所有任务的结果,就在这里for循环拼接拿到的所有<-ch数据
}

func TestGetAnyResponse(t *testing.T) {
	fmt.Println("before: goroutine number is ", runtime.NumGoroutine()) // 2
	fmt.Println(GetResponse())
	time.Sleep(time.Second * 1)
	fmt.Println("afer: goroutine number is ", runtime.NumGoroutine()) // 第一种输出11,第二种输出2
}
  1. buffered Channel实现对象池。

type ReuseableObj struct {
	num int
}
type ObjPool struct {
	bufferChan chan *ReuseableObj
}

func NewObjPool(num int) *ObjPool {
	objPool := ObjPool{}
	objPool.bufferChan = make(chan *ReuseableObj, num)
	for i := 0; i < num; i++ {
		rObj := ReuseableObj{num: i}
		objPool.bufferChan <- &rObj
	}
	return &objPool
}

func (p *ObjPool) GetObj(timeout time.Duration) (*ReuseableObj, error) {
	select {
	case ret := <-p.bufferChan:
		return ret, nil
	default:
		return nil, errors.New("time out")
	}
}

func (p *ObjPool) ReleaseObj(obj *ReuseableObj) error {
	select {
	case p.bufferChan <- obj:
		return nil
	default:
		return errors.New("overflow")
	}
}

func TestChannelPool(t *testing.T) {
	objPool := NewObjPool(10)
	for i := 0; i < 20; i++ {
		if v, err := objPool.GetObj(time.Second * 1); err != nil {
			fmt.Println(err)
		} else {
			fmt.Printf("adderss is %x\n", unsafe.Pointer(&v))
			fmt.Println(v.num)
			if msg := objPool.ReleaseObj(v); msg != nil {
				fmt.Println(msg)
			}
		}
	}
}
  1. sync.Pool的介绍。
  • 获取时先去私有对象中获取,如果不存在就在相同Processor中的共享池中获取,如果还没有,则去其他Processor中去获取。
  • 存放时,如果私有对象不存在,就放在私有对象中,如果存在就放在Processor中。
  • 它其实不能当做对象池去用,因为每次GC都会清空sync.Pool中所有对象,所以这就是为什么上面我们用buffered channel当做对象池来用。
  • 那如果被GC清空了,还要获取的话,怎么办?sync.Pool就会重新初始化一下。
  • 其实,每次如果没有对象,则会开启New方法初始化一下,所以获取的就是999,这里暂时不需要断言了,因为返回的是any
  • 所以,它不适合用来管理自己要定义生命周期的池,因为系统GC不可控。
func TestSyncPool(t *testing.T) {
	pool := &sync.Pool{
		New: func() any {
			fmt.Println("create...")
			return 999
		},
	}
	t.Log(pool.Get()) // 999
	t.Log(pool.Get()) // 999
	t.Log(pool.Get()) // 999
	pool.Put(100)
	t.Log(pool.Get()) // 100
	t.Log(pool.Get()) // 999
	pool.Put(98)
	pool.Put(99)
	t.Log(pool.Get()) //98
	t.Log(pool.Get()) //99
	pool.Put(100)
	runtime.GC()
	t.Log(pool.Get()) //999
}
  1. 我们之前一直在用单元测试在写代码,正常保证文件名_test.go和方法Testxxx即可。在测试代码中如果用t.Fail("xxx")代码其实还会一直执行下去;如果用t.Fatal("xxx")则代码直接中断了。当然还可以借助第三方包进行断言操作。

  2. benchmark和test使用类似,有自己定义的一套语言,如下。

func BenchmarkConcatString(b *testing.B) {
	b.ResetTimer()
	for i := 0; i < 10000; i++ {
	}
	b.StopTimer()
}
// 输出信息包括一共运行次数,每次op用的时间,每次op用到的allocs次数,可以用来比较查找问题
BenchmarkConcatString
BenchmarkConcatString-16
1000000000             0 B/op          0 allocs/op

还有一些BDD的框架,比如goconvey

  1. 使用反射编程。主要是是reflect的几个方法,主要的是如果是获取变量值或者调用方法则后面是ValueOf(),如果是获取信息的则用TypeOf()
func TestReflect(t *testing.T) {
	c := &Course{name: "math", score: 100}
	// 直接访问成员变量:ValueOf + FieldByName
	t.Log(reflect.ValueOf(*c).FieldByName("score")) //100
	t.Log(c)                                        //&{math 100}
	// 访问成员方法:ValueOf + MethodByName
	reflect.ValueOf(c).MethodByName("UpdateScore").Call([]reflect.Value{reflect.ValueOf(60)})
	t.Log(c) //&{math 60}
	// 获取成员变量的一些信息:TypeOf + FieldByName
	if nameField, ok := reflect.TypeOf(*c).FieldByName("name"); ok {
		t.Log(nameField.Tag.Get("format")) // haha
	} else {
		t.Log("get error")
	}
	if nameField1, ok := reflect.TypeOf(*c).FieldByName("score"); ok {
		t.Log(nameField1.Type) // int
	} else {
		t.Log("get error")
	}
}

有一个小知识点,我们的map是不能相互比较的,但可通过reflect.DeepEqual(map1, map2)实现比较。

func TestCmpMap(t *testing.T) {
	map1 := map[int]string{1: "one", 2: "two"}
	map2 := map[int]string{1: "one", 2: "two"}
	//t.Log(map1 == map2)
	t.Log(reflect.DeepEqual(map1, map2)) //true
}

反射编程灵活性高,但性能有损耗,代码可读性也会降低。

  1. unsafe尽量不要使用,除非有些地方必要要使用这种变量类型。

  2. 自带的json解析不如第三方解析包,比如easyjson。但我尝试了一下,还是放弃easyjson,有些麻烦,写个文件,然后使用命令生成个特殊文件里面自动生成好了针对这个对象的marshal等方法,意味着可以直接调用。我还是乖乖使用自带的吧。

type Student struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

var studentStr = `{
	"name": "Eric",
	"age": 18
}`

func TestJson(t *testing.T) {
	s := new(Student)
	if err := json.Unmarshal([]byte(studentStr), s); err != nil {
		t.Log(err)
	} else {
		t.Log(s.Name) //Eric
	}

	if data, err := json.Marshal(s); err != nil {
		t.Log(err)
	} else {
		t.Log(string(data)) //{"name":"Eric","age":18},data是个bytes[]需要转换
	}
}

func TestEnCoderAndDecoder(t *testing.T) {
	s := new(Student)
	decoder := json.NewDecoder(strings.NewReader(studentStr))
	if err := decoder.Decode(s); err != nil {
		t.Log(err)
	} else {
		t.Log(s.Name) //Eric
	}

	encoder := json.NewEncoder(os.Stdout)
	if err := encoder.Encode(s); err != nil { // 直接输出了{"name":"Eric","age":18}
		t.Log(err)
	}
}
  1. 用内置的http可以开启服务。
package main

import (
	"fmt"
	"net/http"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "hello http...")
	})

	http.HandleFunc("/getName", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Eric"))
	})
	http.ListenAndServe(":9090", nil)
}

可以使用第三方路由。

// 安装
go get -u github.com/julienschmidt/httprouter
// 使用GET和POST以及接收参数,POST可以分别接收Form参数和Json参数都是从Request中获取
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"

	"github.com/julienschmidt/httprouter"
)

func GetByIdFunc(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	fmt.Fprint(w, "hello http...")
}

func GetByNameFunc(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	w.Write([]byte("hello " + ps.ByName("name")))
}

type User struct {
	Id   string
	Name string
}

func main() {
	router := httprouter.New()
	router.GET("/", GetByIdFunc)
	router.GET("/getByName/:name", GetByNameFunc)

	router.POST("/submitForm", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
		r.ParseForm()
		w.Write([]byte("hello id:" + r.Form.Get("id") + ";name:" + r.Form.Get("name")))
	})

	router.POST("/submitJson", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
		content := make([]byte, r.ContentLength)
		r.Body.Read(content)
		user := new(User)
		if err := json.Unmarshal(content, user); err == nil {
			w.Write([]byte("hello id:" + user.Id + ";name:" + user.Name))
		} else {
			fmt.Print(err)
			w.Write([]byte("error happened"))
		}
	})

	log.Fatal(http.ListenAndServe(":9090", router))
}
  1. 性能分析。
  • 性能分析通过一些内置工具和第三方工具来分析pprofprofilegraphviz,还可以通过web界面查看。具体参考GO 性能分析。
  • 一些常用指标比如wall timeCPU timeBlock timeMemory allocationGC times等等。
  • 性能差的原因之一,有可能是代码中使用了不恰当的锁。
  1. 编程习惯。
  • 复杂对象(比如数组或者结构体)尽量传递引用。影响系统GC。
  • 初始化时就制定合适的容器大小,避免自动扩容。
  • string拼接,有个strings.Builder。
func TestStringBuilder(t *testing.T) {
	var sb strings.Builder
	sb.WriteString("Hi,")
	sb.WriteString("Eric")
	t.Log(sb.String()) //Hi,Eric
}
  • 注意僵尸进程
  • 考虑使用池优化资源使用
  • 不要害怕错误,尽早抛出错误,给出回应

入门之路结束!切换进阶之路!文章来源地址https://www.toymoban.com/news/detail-691198.html

到了这里,关于Go语言入门记录:从channel的池应用、sync的Pool、benchmark、反射reflect、json处理、http、性能分析和一些编程习惯的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Go并发编程 Goroutine、Channel、Select、Mutex锁、sync、Atomic等

    本文所有实例代码运行go版本: go version go1.18.10 windows/amd64 串行:所有任务一件一件做,按照事先的顺序依次执行,没有被执行到的任务只能等待。最终执行完的时间等于各个子任务之和。 并发:是以交替的方式利用 等待 某个任务的时间来处理其他任务计算逻辑,在计算机

    2024年02月07日
    浏览(52)
  • Go语言中的Pool

    Go语言中的pool是一个资源池,它可以存储一定数量的资源,这些资源可以被多个goroutine共享。Pool可以提高资源的利用率,减少资源的创建和销毁带来的开销。 Pool的实现原理很简单,它使用一个队列来存储资源。当一个goroutine需要使用资源时,它可以从队列中获取一个资源。

    2024年01月17日
    浏览(40)
  • Go语言Sync包

    在处理 goroutine 时,确保它们不会同时访问资源是非常重要的,而 mutex 可以帮助我们做到这一点。 1.1 sync.Mutex 看看这个简单的例子,我没有使用互斥锁来保护我们的变量 a: 此代码的结果是不可预测的,如果幸运的话,您可能会得到 500,但通常结果会小于 500。现在,让我们

    2024年02月16日
    浏览(44)
  • Go语言基准测试(benchmark)三部曲之一:基础篇

    这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos Go的标准库内置的testing框架提供了基准测试(benchmark)功能,可以用来验证本地方法在串行或者并行执行时的基准表现,帮助开发者了解代码的真实性能情况,例如一个方法执行一次的平均耗时,还能

    2024年02月06日
    浏览(50)
  • Go语言基准测试(benchmark)三部曲之三:提高篇

    这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos -《Go语言基准测试(benchmark)三部曲》已近尾声,经历了《基础篇》和《内存篇》的实战演练,相信您已熟练掌握了基准测试的常规操作以及各种参数的用法,现在可以学习一些进阶版的技能了,在面

    2024年02月06日
    浏览(44)
  • Go语言中的Channel

    Channel是Go语言中一种重要的并发原语,它允许goroutine之间安全地交换数据。Channel是一个类型化的队列,它可以存储一个特定类型的值。goroutine可以通过发送和接收操作来向channel中写入和读取数据。 Channel的类型由其元素类型和容量决定。元素类型是指channel中存储的值的类型

    2024年01月17日
    浏览(32)
  • GO语言网络编程(并发编程)Channel

    1.1.1 Channel 单纯地将函数并发执行是没有意义的。函数与函数间需要交换数据才能体现并发执行函数的意义。 虽然可以使用共享内存进行数据交换,但是共享内存在不同的goroutine中容易发生竞态问题。为了保证数据交换的正确性,必须使用互斥量对内存进行加锁,这种做法势

    2024年02月09日
    浏览(72)
  • Golang中sync.Pool详解及使用方法

    sync.Pool是用来保存可以被重复使用的临时对象,以便在以后的同类操作中可以重复使用,从而避免了反复创建和销毁临时对象带来的消耗以及对GC造成的压力。常用池化技术来提高程序的性能,例如连接池、线程池等。sync.Pool是并发安全的,可以在多个goroutine中并发调用sync

    2024年02月02日
    浏览(34)
  • 自然语言处理从入门到应用——LangChain:记忆(Memory)-[聊天消息记录]

    分类目录:《大模型从入门到应用》总目录 LangChain系列文章: 基础知识 快速入门 安装与环境配置 链(Chains)、代理(Agent:)和记忆(Memory) 快速开发聊天模型 模型(Models) 基础知识 大型语言模型(LLMs) 基础知识 LLM的异步API、自定义LLM包装器、虚假LLM和人类输入LLM(

    2024年02月12日
    浏览(46)
  • 100天精通Golang(基础入门篇)——第12天:深入解析Go语言中的集合(Map)及常用函数应用

    🌷 博主 libin9iOak带您 Go to Golang Language.✨ 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍》学会IDEA常用操作,工作效率翻倍~💐 🪁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬请批

    2024年02月12日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包