分类目录归档:golang

golang标准库 —— strconv包

strconv包为我们提供的方法可以分校几类:

  • Append类
  • Format类
  • Parse类
  • Quote类
  • 其他

Append类

将各种类型转换为字符串后追加到 dst 尾部。

示例:

将各种类型转换为带引号字符串后追加到 dst 尾部。

示例:

Format类

Format类的方法主要是格式化对应类型的值,然后返回字符串

示例:

Parse类

这些类型的方法提供了转换类型的功能,一般都会返回第二个返回值错误类型

示例:

Quote类

示例:

其他

对基础方法的再封装

将整数转换为十进制字符串形式(即:FormatInt(i, 10) 的简写)

将字符串转换为十进制整数(即: ParseInt(s, 10, 0) 的简写)

示例

判断字符串是否可以不被修改的表示为一个单行的反引号字符串。
字符串中不能含有控制字符(除了 \t)和“反引号”字符,否则返回 false

示例:

判断 r 是否为可打印字符

示例:

转载参考:https://blog.csdn.net/wangshubo1989/article/details/75222002

golang标准库 —— errors包

error 接口

Go语言引入了一个关于错误处理的标准模式,即error接口,该接口定义:

对于大多数函数,如果要返回错误,大致可以定义为如下模式,将error作为多种返回值中的最后一个,但这并非是强制要求:

通过errors下的New方法可以创建一个错误:

error 源码分析

errors/errors.go下定义了errorString,并且让其实现了Error方法。

所以New方法创建的errorString是一个error类型。

实现自己的错误类型

golang标准库 —— container包

container包实现了三个复杂的数据结构:堆(heap),链表(list),环(ring)

链表-List

List结构体为链表本身,Element结构体是链表下的每一个元素。

Element

Element 结构体包含上一个元素和下一个元素的指针指向,所属List的指向,以及当前Element的值。

Element 提供了两个方法,获取上一个元素和下一个元素。

List

List本身包含了root Element主节点以及链表的长度。

创建一个List

创建List时,会进行初始化将root的上下节点都指向root本身。

回过头来我们会发现Element获取上下元素时会判断,下一个或者上一个元素是否root本身,如果是这回返回nil

List操作抽象

链表的操作我们都可以抽象为insertremove

insert,在指定节点插入某个节点。

  • 将上一个节点的next指向插入节点,将下一个节点的的prev指向插入节点
  • 将插入节点的next指向下一个节点,将插入节点的的prev指向上一个节点
  • 列表长度++, 插入节点指向list

remove, 操作起来就更简单了,直接将上下两个节点互指就可以了,然后放置内存泄漏清空删除节点的指向关系。

有了insertremove两个操作以后,我们可以扩展衍生到所有操作。在这里找几个比较典型的例子来说,

  • 移动至最前端:先将指定节点移除,然后将节点插入到root节点后

  • 移动至最后个节点:将节点移除,然后插入到root节点的上一个几点。

我们可以套用l.insert(l.remove(e), &Element{}),然后将节点移动到任意各节点去。

环-Ring

结构体Ring指的是环上的节点。相比于List它并没有元素与结构之分。Ring首位相连,形成一个环。

New

创建一个拥有10各节点的环。

Link & Unlink

Link将两个环相连,原理与链表的插入类似。

  • 将环A的尾与环B的头相连
  • 将环A的头与环B的尾相连

Unlink移除环中的某一段。

这里它的使用了一个技巧,先用Move方法获取第N+1个节点,然后将该节点传入Link方法,是第一个节点与第第N+1个节点相连,那么第2个节点到第N个节点就被删除了。

Do

循环ring操作。

堆-heap

heap下只有interface,并没有struct的实现,所以需要我们去实现。

heap.Interface嵌套了sort.Interface,所以我们一共需要实现5个方法,

我们自定义一个IntHeap,然后让其完成heap的一系列操作。

在定义好结构体,以及实现接口之后,我们对其进行操作,发现IntHeap自动实现了最小堆的功能。

为什么会这样呢?

主要因为heap中有两个函数up上浮和down下沉。

当我们操作heap的时候,会根据情况自动调用updown方法,完成最小堆的排序。

updown方法中,调用了我们实现的LessSwap方法取做相关操作。

参考:

  • https://books.studygolang.com/The-Golang-Standard-Library-by-Example/chapter03/03.3.html
  • https://ieevee.com/tech/2018/01/29/go-heap.html

golang new 和 make 区别

new(T) 返回的是 T 的指针

new(T) 为一个 T 类型新值分配空间并将此空间初始化为 T 的零值,返回的是新值的地址,也就是 T 类型的指针 *T,该指针指向 T 的新分配的零值。

上面的代码是等价的,new(int) 将分配的空间初始化为 int 的零值,也就是 0,并返回 int 的指针,这和直接声明指针并初始化的效果是相同的。

make 只能用于 slice,map,channel

make 只能用于 slice,map,channel 三种类型,make(T, args) 返回的是初始化之后的 T 类型的值,这个新值并不是 T 类型的零值,也不是指针 *T,是经过初始化之后的 T 的引用。

slice 的零值是 nil,使用 make 之后 slice 是一个初始化的 slice,即 slice 的长度、容量、底层指向的 array 都被 make 完成初始化,此时 slice 内容被类型 int 的零值填充,形式是 [0 0 0],map 和 channel 也是类似的。

make(T, args) 返回的是 T 的 引用

如果不特殊声明,go 的函数默认都是按值穿参,即通过函数传递的参数是值的副本,在函数内部对值修改不影响值的本身,但是 make(T, args) 返回的值通过函数传递参数之后可以直接修改,即 map,slice,channel 通过函数穿参之后在函数内部修改将影响函数外部的值。

这说明 make(T, args) 返回的是引用类型,在函数内部可以直接更改原始值,对 map 和 channel 也是如此。

很少需要使用 new

以下代码演示了 struct 初始化的过程,可以说明不使用 new 一样可以完成 struct 的初始化工作。

foo1 和 foo2 是同样的类型,都是 Foo 类型的值,foo1 是通过 var 声明,Foo 的 filed 自动初始化为每个类型的零值,foo2 是通过字面量的完成初始化。foo3,foo4 和 foo5 是一样的类型,都是 Foo 的指针 Foo。*但是所有 foo 都可以直接使用 Foo 的 filed,读取或修改,为什么?

如果 x 是可寻址的,&x 的 filed 集合包含 m,x.m 和 (&x).m 是等同的,go 自动做转换,也就是 foo1.age 和 foo3.age 调用是等价的,go 在下面自动做了转换。

因而可以直接使用 struct literal 的方式创建对象,能达到和 new 创建是一样的情况而不需要使用 new。

小结

new(T) 返回 T 的指针 *T 并指向 T 的零值。

make(T) 返回的初始化的 T,只能用于 slice,map,channel。

转载自: http://sanyuesha.com/2017/07/26/go-make-and-new/

golang标准库 —— context包

一、背景描述

Golang中我们可以使用sync.WaitGroup进行并发控制。如下,

wg.Wait()会等待2个goroutine完成后继续执行。

在实际的业务中,我们可能会有这么一种场景:需要我们主动的通知某一个goroutine结束。比如我们开启一个后台goroutine一直做事情,比如监控,如果主程序,就需要通知这个监控goroutine结束,不然它会一直跑,就泄漏了。

chan通知

golang一直推崇着不要通过共享内存来通信,而应该通过通信来共享内存,我们可以使用select + chan的方式来实现。

当主程序完成后,向stop chan中推送消息,goroutine接收到消息后推出监控。

context

Context包是Google内部专门用来简化 对于处理单个请求的多个 goroutine 之间与请求域的数据、取消信号、截止时间等相关操作,这些操作可能涉及多个 API 调用。在Golang1.7版本之后开源出来了。

1.6及之前版本可以参考golang.org/x/net/context

我们可以使用context包的功能里替换select + chan的模式。但是其本质还是select + chan。如下,

  • context.Background() 获取一个空的context
  • ctx, cancel := context.WithCancel(context.Background())扩展一个带有取消功能的coontext,同时得到的cancel是以后取消的方法。当需要执行取消操作的时候,cancel()执行一下就可以了。
  • ctx.Done()获取一个chan。当chan里面可以获取到东西的时候,就表示该context已经被取消了。

context也是线程安全的,所以也支持传入多个goroutine

二、源码剖析

核心接口

emptyCtx

Context接口并不需要我们自己来实现,因为context包内已经帮我们实现了一个emptyCtx

emptyCtx是一个不可取消,没有设置截止时间,没有携带任何值的Context

background & todo

context提供了2个方法来获取emptyCtxbackgroudtodo本质上没有区别,一般通过要干什么事情来区分调用哪一个。

  • background,主要用于main函数、初始化以及测试代码中
  • todo,当你不清楚需要使用那个时,就用它

WithCancel、 WithDeadline、 WithTimeout、 WithValue

当有了emptyCtx以后,我们可以使用golang为我们提供的4个扩展方法,对emptyCtx进行扩展。如下,

1、WithCancel

WithCancel函数,传递一个父Context作为参数,返回cancelCtx,以及一个CancelFunc用来取消Context

使用方法正如上面的监控案例。

2、WithDeadline、WithTimeout

WithDeadline它会多传递一个截止时间参数,意味着到了这个时间点,会自动取消Context,当然我们也可以不等到这个时候,可以提前通过取消函数进行取消。

WithTimeout底层调用的WithDeadline 区别就是一个传入的具体是简单,一个传入的是偶多少时间之后。

上面案例,当时间到了之后,就自动停止了。当我么你再去调用cancel方法时,会自动忽略。

3、WithValue

通过Context我们也可以传递一些必须的元数据,这些数据会附加在Context上以供使用。

取值时,key必须对应上,否则返回nil

golang channel 学习笔记

一、 chan 的类型

channel类型是可以带有方向的,假设T是一种类型:

  • chan T是双向channel类型,编译器允许对双向channel同时进行发送和接收。
  • chan<- T是只写channel类型,编译器只允许往channel里面发送数据。
  • <-chan T是只读channel类型,编辑器只允许从channel里面接收数据。

双向类型的channel,可以被强制转换成只读channel或者是只写channel,但是反过来却不行,只读和只写channel是不可以转换成双向channel的。

二、 chan 的初始化

channel里面的value buffer的容量也就是channel的容量。channel的容量为零表示这是一个阻塞型通道,非零表示缓冲型通道[非阻塞型通道]。

三、内部结构

每个channel内部实现都有三个队列

  • 接收消息的协程队列。这个队列的结构是一个限定最大长度的链表,所有阻塞在channel的接收操作的协程都会被放在这个队列里。
  • 发送消息的协程队列。这个队列的结构也是一个限定最大长度的链表。所有阻塞在channel的发送操作的协程也都会被放在这个队列里。
  • 环形数据缓冲队列。这个环形数组的大小就是channel的容量。如果数组装满了,就表示channel满了,如果数组里一个值也没有,就表示channel是空的。对于一个阻塞型channel来说,它总是同时处于即满又空的状态。

四、相关操作

五、select-case

上面这段代码中,select 语句有四个 case 子语句,前两个是 receive 操作,第三个是 send 操作,最后一个是默认操作。代码执行到 select 时,case 语句会按照源代码的顺序被评估,且只评估一次,评估的结果会出现下面这几种情况:

  • 除 default 外,如果只有一个 case 语句评估通过,那么就执行这个case里的语句;
  • 除 default 外,如果有多个 case 语句评估通过,那么通过伪随机的方式随机选一个;
  • 如果 default 外的 case 语句都没有通过评估,那么执行 default 里的语句;
  • 如果没有 default,那么 代码块会被阻塞,指导有一个 case 通过评估;否则一直阻塞

下面会随机输出 v1, v2

下面会循环输出全部,直至主进程销毁。

超时设定

搭建GOLANG环境

1、下载

2、安装

3、设置

4、查看版本

golang单元测试 —— goconvey

一、库简介

Go 语言虽然自带单元测试功能,在 GoConvey 诞生之前也出现了许多第三方辅助库。但没有一个辅助库能够像 GoConvey 这样优雅地书写代码的单元测试,简洁的语法和舒适的界面能够让一个不爱书写单元测试的开发人员从此爱上单元测试。

二、下载安装

三、api文档

请移步至 https://github.com/smartystreets/goconvey/wiki/Documentation

四、基本使用方法

被测试代码

测试代码

开始测试
使用命令行go test -v
得到测试结果

五、web界面

想要使用 GoConvey 的 Web 界面特性,需要在相应目录下执行 goconvey(需使用 go get 安装到 $GOPATH/bin 目录下),然后打开浏览器,访问 http://localhost:8080 ,就可以看到相应界面。
goconvey界面