![f1809218d0b5e41a907528007257d13c.png](https://img-blog.csdnimg.cn/img_convert/f1809218d0b5e41a907528007257d13c.png)
Go语言基础(十一)
一、并发安全和锁
二、HTTP客户端和服务端
三、单元测试
四、性能基准测试
一、并发安全和锁
在某些场景下可能会存在多个goroutine同时操作一个资源(临界区),这种情况会发发生竞态问题,举个例子:
package
两个goroutine去累加变量x,这两个goroutine在访问和修改x变量的时候就会存在数据竞争。因此就需要锁。
1、互斥锁
互斥锁是一种常用的控制共享资源访问的方法。它能够保证同时只有一个goroutine可以访问共享资源,Go语言中通过sync包中的Mutex类型来实现互锁。sync.Mutex是一个结构体,值类型,给函数传参的时候需要传指针。修改上述代码。
var
使用互斥锁能够保证同一时间有且只有一个goroutine进入临界区,其他的goroutine则在等待,如果有多个goroutine等待,下一次将在这些goroutine中随机选择一个进入临界区。
2、读写互斥锁
互斥锁是完全互斥的,但是很多场景下是读多写少,当并发地去读一个资源不涉及修改的时候是没有必要加锁的。读写锁在Go语言中使用sync包中的RWMutex类型。
package
需要注意的是读写锁非常适合读多写少的场景,如果读写的操作差别不大,读写锁的优势就发挥不出来。
3、sync.WaitGroup
![264ba72bba3281d572c354c7c1b841f5.png](https://img-blog.csdnimg.cn/img_convert/264ba72bba3281d572c354c7c1b841f5.png)
4、sync.Once
Go语言中sync包提供了一个针对执行一次的场景的解决方案,sync.Once只有一个Do方法。
5、sync.Map
Go语言中内置的map并不是安全的。举个例子:
package
我开启了2000个也没报错,不过为什么,反正这个就是不安全?
在Go语言中sync提供了一个开箱即用的安全版map,同时sync.Map内置了诸如Store、Load、LoadOrStore、Delete、Range等方法。
package
6、原子操作
代码中加锁操作因为涉及内核切换上下文耗时、代价比较高,针对基本数据类型可以使用原子操作确保并发安全,Go语言中原子操作由内置的标准库sync/atomic提供。atomic提供了原子级别的内存操作。
二、HTTP客户端和服务端
Go语言内置的net/http包提供了HTTP客户端和服务端的实现。
1、http协议
超文本传输协议(HyperText Transfer Protocol)是互联网上最为广泛应用的一种网络传输协议。所有的www文件都必须遵守这个标准,设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。
2、http服务器端
get请求示例:使用net/http包编写一个简单的发送http请求的客户端:
package
3、http客户端
package
另一种形式:
package
4、小结
对于http和网络编程只是初识。后续如果用到再仔细看看
三、单元测试
不写测试的开发不是好程序员。
Go语言中测试依赖于go test命令,go test是按照一定约定和组织的测试代码的驱动程序,在包目录内,所有以_test.go为后缀名的源代码文件都是go test测试的一部分,不会被go build编译到最终的可执行文件中。
在*_test.go文件中有三种类型的函数,单元测试函数、基准测试函数和示例函数。
![afbf8e1748182b4e32f51ceb89dbfaa4.png](https://img-blog.csdnimg.cn/img_convert/afbf8e1748182b4e32f51ceb89dbfaa4.png)
go test命令会遍历所有的*_test.go文件中符合上述命名规则的函数,然后生成一个临时的main包用于调用相应的测试函数,然后构建并运行、报告测试结果,最后清理测试文件中的临时文件。
1、测试函数示例
新建一个字符串分割功能的文件夹split_string,新建文件split.go,用于切割字符串:
package
新建测试函数,split_test.go
package
直接在终端中打开,然后执行go test命令或者go test -v命令。
2、测试组
package
3、子测试
package
![cbb0385aa0a37315dd851978983275b4.png](https://img-blog.csdnimg.cn/img_convert/cbb0385aa0a37315dd851978983275b4.png)
单独跑其中一个测试:go test -run=测试名
![2dc1312833106495f953851cc20f9ca3.png](https://img-blog.csdnimg.cn/img_convert/2dc1312833106495f953851cc20f9ca3.png)
4、测试覆盖率
测试覆盖率,测试中被至少被运行一次的代码占总代码的比例。Go提供了内置功能来检查 覆盖率,可以使用go test -cover,例如上面自测试的测试代码:
![60cf99637a09b50c74cdda307cd3de9a.png](https://img-blog.csdnimg.cn/img_convert/60cf99637a09b50c74cdda307cd3de9a.png)
此外,Go还提供了一个额外-coverprofile参数,用来将覆盖率相关的记录信息输出到一个文件。例如:
![8833f1de6e8e4ddd056bff724cff6553.png](https://img-blog.csdnimg.cn/img_convert/8833f1de6e8e4ddd056bff724cff6553.png)
然后执行go tool cover -html=c.out,使用cover工具来处理生成的记录,该命令会打开一个本地浏览器窗口生成一个html报告。
![596c2870e201fe1e1d7347aa44fa5e0a.png](https://img-blog.csdnimg.cn/img_convert/596c2870e201fe1e1d7347aa44fa5e0a.png)
四、性能基准测试
5、基准测试
基准测试是在一定的工作负载之下检测程序性能的一种方法。基准测试并不会默认执行,需要增加-betch参数,所以使用go test -bench=Split命令执行基准测试。
func
![024635a803f44995b69a2a0db841bab1.png](https://img-blog.csdnimg.cn/img_convert/024635a803f44995b69a2a0db841bab1.png)
BenchmarkSplit-12表示对Split函数进行基准测试,数字12表示GOMAXPROCS的值。
1000000和4420 ns/op表示每次调用Split函数耗时4420ns,这个结果是1000000次调用的结果。
还可以为基准测试添加-benchmem参数,来获得内存分配统计情况。
![c771a4fcb0cf68c0ea428c7f80c5a183.png](https://img-blog.csdnimg.cn/img_convert/c771a4fcb0cf68c0ea428c7f80c5a183.png)
其中,240B/op表示每次操作内存分配240字节,4 alloc/op表示每次操作进行4次内存分配。对Split函数优化如下:
func
![7a35e36502519d85e0be9873c966a838.png](https://img-blog.csdnimg.cn/img_convert/7a35e36502519d85e0be9873c966a838.png)
2、性能比较函数
使用斐波那契举例:
package
性能比较函数:
package
执行go test -bench=Fib2,默认情况下,每个基准测试至少运行1s,如果Benchmark函数返回时没有到1s,则b.N会按1,2,5,10,20,50,...增加,并且函数再次运行。
![d5c2421e547f2407f3990c986f268391.png](https://img-blog.csdnimg.cn/img_convert/d5c2421e547f2407f3990c986f268391.png)
还可以使用-benchtime指定最小基准时间,强制执行,以产生更准确的结果。
3、重置时间
b.ResetTimer之前的处理不会放到执行时间里,也不会输出到报告中,所以可以在之前做一些不计划作为测试报告的操作,比如连接数据库的时间。例如:
func
4、并行测试
func
RunParallel会创建出多个goroutine,并将b.N分配给这些goroutine执行,其中goroutine默认值为GOMAXPROCS,如果用户需要增加CPU限制可以使用SetParallelism。RunParallel通常会与-cpu标志一同使用。
func
![943af2063b8e34fcd06fc2f0790572f5.png](https://img-blog.csdnimg.cn/img_convert/943af2063b8e34fcd06fc2f0790572f5.png)
还可以在测试命令后面添加-cpu参数如go test -bench=. -cpu 1来指定cpu的数量。
5、Setup和TearDown
测试程序有时需要在测试之前进行额外的设置setup或在测试之后进行拆卸teardown。
通过在*_test.go文件中定义TestMain函数来可以在测试之前进行额外的设置setup或在测试之后进行拆卸teardown操作。
func
举个例子:
func
![ce91fbdac81953f8d232d96bf587175c.png](https://img-blog.csdnimg.cn/img_convert/ce91fbdac81953f8d232d96bf587175c.png)
6、示例函数
编写示例函数的好处:
1、示例函数能够作为文档直接使用,例如基于web的godoc中能把示例函数与对应的函数或包关联。
2、示例函数只包含了//output:也是可以通过go test运行的可执行测试。
3、示例函数提供了可直接运行的示例代码,可以直接在http://golang.org的godoc文档服务器上使用GoPlayGround运行示例代码。
我是尾巴~
每日一句毒鸡汤:很多人的生活都是一望无际、广袤无垠,感觉朋友如星星点头般布满生命的天幕, 其实每颗星与星之间的距离却那么遥远。
本次推荐:
Download DiffDogwww.altova.com继续加油~!