vendor】 Golang包管理【golang】


下载vendor包

go get -u github.com/kardianos/govendor

go build 生成govendor文件

执行 ./govendor init生成vendor目录

执行 ./vendor add +external 导入依赖包

govendor add +e

lorca】 golang图像界面库【golang】


用于在 Go 中构建现代 HTML5 桌面应用程序. 它使用 Chrome 浏览器作为 UI 层

Github

go】 又称Golang,静态强类型、编译型语言【golang】


Go是Google公司免费开源的程序语言,而且go是管理源代码的工具.

官网: golang.org

国内下载:https:studygolang.com/dl

Go基础语法宝典

Go语言参考手册

相关环境变量

GOPATH 用户路径

GOROOT GO系统路径

GO111MODULE 是否使用module

下载并安装包

go get package_path

编译并运行源文件(它必须包含一个main包)

go run file.go

编译当前目录存在的包

go build

执行当前包的所有测试实例(文件名必须_test.go结尾)

go test

编译并安装当前包

go install

编译小化

-s去掉符号表 -w去掉DWARF调试信息,得到的程序就不能用gdb调试了。

go build -ldflags "-s -w"

GO111MODULE

GO111MODULE=off 无模块支持,go 会从 GOPATH 和 vendor 文件夹寻找包

GO111MODULE=on 模块支持,go 会忽略 GOPATH 和 vendor 文件夹,只根据 go.mod 下载依赖

GO111MODULE=auto 在 GOPATH/src 外面且根目录有 go.mod 文件时,开启模块支持

设置代理

go env -w GO111MODULE=on

go env -w GOPROXY=https:goproxy.cn,direct

export GOPROXY=https:mirrors.aliyun.com/goproxy/

export GOPROXY=https:gonexus.dev

export GOPROXY=https:goproxy.io/

export GOPROXY=https:athens.azurefd.net

export GOPROXY=https:goproxy.cn

export GOPROXY=https:proxy.golang.org

export GOPROXY=https:gocenter.io

关键字

break default func interface select

case defer go map struct

chan else goto package switch

const fallthrough if range type

continue for import return var

数据类型的定义

var variableName type

var vname1, vname2, vname3 type

var variableName type = value

var vname1, vname2, vname3 type= v1, v2, v3

var vname1, vname2, vname3 = v1, v2, v3

vname1, vname2, vname3 := v1, v2, v3

goto

Go有goto语句——请明智地使用它。用goto跳转到必须在当前函数内定义的标签。例如假设这样一个循环:


func myFunc() {
  
    i := 0
  
Here:   //这行的第一个词,以冒号结束作为标签
  
    println(i)
  
    i++
  
    goto Here   //跳转到Here去
  
}
  

标签名是大小写敏感的。

函数作为值、类型

在Go中函数也是一种变量,可以通过type来定义它,它的类型就是所有拥有相同的参数,相同的返回值的一种类型

type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...])

Hello World


package main
  

  
import "fmt"
  

  
func main() {
  
	fmt.Println("hello world")
  
}
  

Values

Go 有多种值的类型,包括 string,integer,float,boolean 等。如下是几个基本例子。


package main
  

  
import "fmt"
  

  
func main() {
  
	//string可以使用+连接在一起
  
	fmt.Println("go" + "lang")
  
	fmt.Println("1+1=", 1+1)
  
	fmt.Pritnln("7.0/3.0=", 7.0/3.0)
  
	fmt.Println(true && false)
  
	fmt.Println(true || false)
  
	fmt.Pritnln(!true)
  
}
  

Variables

在 Go 中,变量被编译器显式的声明和使用,例如检查函数调用类型的正确性。



  
import "fmt"
  

  
func main() {
  
	//声明一个或多个变量
  
	var a string = "initial"
  
	fmt.Println(a)
  
	var b, c int = 1, 2
  
	fmt.Println(b, c)
  
	//Go将推断变量的初始化类型
  
	var d = true
  
	fmt.Println(d)
  
	//声明没有初始值的变量将被初始化为零值,如int的零值是0
  
	var e int
  
	fmt.Println(e)
  
	//var f string = "short"的简化写法
  
	f := "short"
  
	fmt.Println(f)
  
}
  

Constants

Go 支持的常量有字符、字符串、布尔值以及数值


package main
  

  
import "fmt"
  
import "math"
  

  
const s string = "constant"
  

  
func main() {
  
	fmt.Println(s)
  
	//const声明可以出现在任何var声明出现的地方
  
	const n = 50000000
  
	//常量表达式可以任意精度进行运算
  
	const d = 3e20 / n
  
	fmt.Println(d)
  
	//数值常量没有类型,除非进行了显式转换等类型赋予
  
	fmt.Println(int64(d))
  
	//数值在使用环境上下文中会得到类型,如变量赋值或者方法调用,如math.Sin需要的是一个float64
  
	fmt.Println(math.Sin(n))
  
}
  

  

For

for 是 Go 中唯一的循环结构,下面是三种基本的 for 循环类型。


package main
  

  
import "fmt"
  

  
func main() {
  
	i := 1
  
	//最基本的类型,只有一个条件
  
	for i <= 3 {
  
		fmt.Println(i)
  
		i = i + 1
  
	}
  
	//经典的 初始化/条件/循环后 for循环
  
	for j := 7; j <= 9; j++ {
  
		fmt.Println(j)
  
	}
  
	//没有条件的for语句将无限循环,除非在内部的方法中使用了break或者return
  
	for {
  
		fmt.Println("loop")
  
		break
  
	}
  
	//也可以使用continue来直接到下一个循环
  
	for n := 0; n <= 5; n++ {
  
		if n%2 == 0 {
  
			continue
  
		}
  
		fmt.Println(n)
  
	}
  
}
  

If/Else


package main
  

  
import "fmt"
  

  
func main() {
  
	if 7%2 == 0 {
  
		fmt.Println("7 is even")
  
	} else {
  
		fmt.Println("7 is odd")
  
	}
  
	//可以单独使用if
  
	if 8%4 == 0 {
  
		fmt.Println("8 is divisible by 4")
  
	}
  
	//条件之前可以有语句,声明在该语句中的任何变量可以用于所有分支
  
	if num := 9; num < 0 {
  
		fmt.Println(num, "is negative")
  
	} else if num < 10 {
  
		fmt.Println(num, "has 1 digit")
  
	} else {
  
		fmt.Println(num, "has multiple digits")
  
	}
  
}
  

注意,Go 中条件周围不需要圆括号,但是花括号是必要的。

Go 中没有三目 if 语句,所以对于最基本的条件也需要写完整的 if 语句。

Switch


package main
  

  
import "fmt"
  
import "time"
  

  
func main() {
  
	i := 2
  
	fmt.Print("write", i, " as ")
  
	switch i {
  
	case 1:
  
		fmt.Println("one")
  
	case 2:
  
		fmt.Println("two")
  
	case 3:
  
		fmt.Println("three")
  
	}
  
	//可以在同一个case语句中使用逗号来分隔多个表达式
  
	switch time.Now().weekday() {
  
	case time.Saturday, time.Sunday:
  
		fmt.Println("it's the weekend")
  
	//这里也使用了default case
  
	default:
  
		fmt.Println("it's a weekday")
  
	}
  
	t := time.Now()
  
	//没有表达式的switch是另一种实现if/else逻辑的路子,同时这也展示了case表达式可以是非常量值。
  
	switch {
  
	case t.Hour() < 12:
  
		fmt.Println("it's before noon")
  
	default:
  
		fmt.Println("it's after noon")
  
	}
  
	//类型switch比较了类型而非值
  
	whatAmI := func(i interface{}) {
  
		switch t := i.(type) {
  
		case bool:
  
			fmt.Println("I'm a bool")
  
		case int:
  
			fmt.Println("I'm an int")
  
		default:
  
			fmt.Println("Don't know type %T\n", t)
  
		}
  
	}
  
	whatAmI(true)
  
	whatAmI(1)
  
	whatAmI("hey")
  
}
  

Arrays

在 Go 中,数组是特定长度元素的编号序列。


package main
  

  
import "fmt"
  

  
func main() {
  
	//这里创建了一个5个int元素的数组。默认是零值,对于int而言就是0
  
	var a [5]int
  
	fmt.Println("emp:", a)
  
	//可以通过array[index]=value语法设值,或者通过array[index]取值
  
	a[4] = 100
  
	fmt.Println("set:", a)
  
	fmt.Println("get:", a[4])
  
	//内置的len函数返回数组的长度
  
	fmt.Println("len:", len(a))
  
	//声明并初始化数组
  
	b := [5]int{1, 2, 3, 4, 5}
  
	fmt.Prinln("dc1:", b)
  
	//数组是一维的,但你可以组合类型来构建多维数组结构
  
	var twoD [2][3]int
  
	for i := 0; i < 2; i++ {
  
		for j := 0; j < 3; j++ {
  
			twoD[i][j] = i + j
  
		}
  
	}
  
	fmt.Println("2d: ", twoD)
  
}
  

Slices

slice 是一个重要的数据类型,对于序列提供了比数组更强大的接口


package main
  

  
import "fmt"
  

  
func main() {
  
	//与数组不同,切片仅由其包含的元素(不是元素的数量)键入。
  
	//要创建一个非零长度的空切片,请使用内置的make。 这里我们制作长度为3的字符串(最初为零值)。
  
	s := make([]string, 3)
  
	fmt.Println("emp:", s)
  
	//可以像数组一样set和get
  
	s[0] = "a"
  
	s[1] = "b"
  
	s[2] = "c"
  
	fmt.Println("set:", s)
  
	fmt.Println("get:", s[2])
  
	//len能够返回slice的长度
  
	fmt.Println("len:", len(s))
  
	//作为这些基本操作的补充,slice支持一些令其比数组更丰富的东西。
  
	//其中一个是append函数,其返回一个包含一个或多个新值的slice
  
	//注意需要接受append的返回值来获取新的slice值
  
	s = append(s, "d")
  
	s = append(s, "e", "f")
  
	fmt.Println("apd:", s)
  
	//slice可以复制
  
	c := make([]string, len(s))
  
	copy(c, s)
  
	fmt.Println("cpy:", c)
  
	//slice支持切片操作,语法为slice[low:high]。如下将得到元素s[2],s[3]和s[4]
  
	l := s[2:5]
  
	fmt.Println("sl1:", l)
  
	//如下切片截止到(不包含)s[5]
  
	l = s[:5]
  
	fmt.Pritnln("sl2:", l)
  
	//如下切片从s[2]开始(包含)
  
	l = s[2:]
  
	fmt.Println("sl3:", l)
  
	//声明并初始化slice
  
	t := []string{"g", "h", "i"}
  
	fmt.Println("dcl:", t)
  
	//slice也可以组织成一个多为数据结构,内部的slice长度可以变化,这与多维数组不同
  
	twoD := make([][]int, 3)
  
	for i := 0; i < 3; i++ {
  
		innerLen := i + 1
  
		twoD[i] = make([]int, innerLen)
  
		for j := 0; j < innerLen; j++ {
  
			twoD[i][j] = i + j
  
		}
  
	}
  
	fmt.Println("2d: ", twoD)
  
}
  

Maps

Map 是 Go 内置的关联数据类型(其他语言可能成为哈希或者字典)


package main
  

  
import "fmt"
  

  
func main() {
  
	//要创建一个空的map,使用内置make函数:make(map[key-type]val-type)
  
	m := make(map[string]int)
  
	//通过经典的name[key]=val语法来设置key/value对
  
	m["k1"] = 7
  
	m["k2"] = 13
  
	fmt.Println("map:", m)
  
	//通过name[key]来获取一个value
  
	v1 := m["k1"]
  
	fmt.Println("v1: ", v1)
  
	//len函数返回map中键值对的个数
  
	fmt.Println("len:", len(m))
  
	//内置的delete函数将移除map中的键值对
  
	delete(m, "k2")
  
	fmt.Println("map:", m)
  
	//第一个值是该key的value,但此处不需要,故使用空白占位符“_”忽略
  
	//第二个可选的返回值表明该键是否在map中,这样可以消除不存在的键,和键值为0或者""的歧义
  
	_, prs := m["k2"]
  
	fmt.Println("prs:", prs)
  
	//声明并初始化map
  
	n := map[string]int{"foo": 1, "bar": 2}
  
	fmt.Println("map:", n)
  
}
  

map 将以[ k:v k:v ]的形式打印

Range

range 可以遍历各种数据结构中的元素。


package main
  
import "fmt"
  
func main(){
  
  //这里使用range来计算元素的和,数组也是类似的用法
  
  nums:=[]int{2,3,4}
  
  sum:=0
  
  for _,num:=range nums{
  
    sum+=num
  
  }
  
  fmt.Println("sum:",sum)
  
  //在slice和array上的range均为每个条目提供了索引和值
  
  for i,num:=range nums{
  
    if num==3{
  
      fmt.Pritnln("index",i)
  
    }
  
  }
  
  //map上的range通过key/value对进行遍历
  
  kvs:=map[string]string{"a":"apple","b":"banana"}
  
  for k,v:=range kvs{
  
    fmt.Printf("%s -> %s\n",k,v)
  
  }
  
  //range可以仅通过key进行遍历
  
  for k:= range kvs{
  
    fmt.Println("key:",k)
  
  }
  
  //range作用在string上将得到unicode code points,第一个值是字符的起始字节索引,第二个值是字符本身
  
  for i,c:range "go" {
  
    cmt.Println(i,c)
  
  }
  
}
  

  

Functions


ackage main
  

  
import "fmt"
  

  
//这个函数接收两个int并以int类型返回他们的和
  
func plus(a int, b int) int {
  
	//Go需要显式的return语句,它不会自动返回最后一个表达式的值
  
	return a + b
  
}
  

  
//如果有多个连续的相同类型的参数,可以忽略前面的类型声明
  
func plusPlus(a, b, c int) int {
  
	return a + b + c
  
}
  
func main() {
  
	//如你所想的那样调用函数
  
	res := plus(1, 2)
  
	fmt.Println("1+2=", res)
  
	res = plusPlus(1, 2, 3)
  
	fmt.Println("1+2+3=", res)
  
}
  

Multiple Return Values

Go 内置支持了返回多个值。这一特点经常用于 Go 的习惯用法,例如同时返回结果和错误值


package main
  

  
import "fmt"
  

  
//方法签名中的(int,int)表明它将返回两个int值
  
func vals() (int, int) {
  
	return 3, 7
  
}
  
func main() {
  
	//这里我们通过多重赋值来使用两个不同的返回值
  
	a, b := vals()
  
	fmt.Println(a)
  
	fmt.Println(b)
  
	//如果只需要返回结果的子集,使用空白占位符_
  
	_, c := vals()
  
	fmt.Println(c)
  
}
  

Variadic Functions

变参函数,可以使用任意数量的参数来进行调用。例如 fmt.Println 是一个常见的变参函数。


package main
  

  
import "fmt"
  

  
//这是一个接收任意数量的int值的函数
  
func sum(nums ...int) {
  
	fmt.Print(nums, " ")
  
	total := 0
  
	for _, num := range nums {
  
		total += num
  
	}
  
	fmt.Println(total)
  
}
  
func main() {
  
	sum(1, 2)
  
	sum(1, 2, 3)
  
	//如果你已经在一个slice中定义了多个参数,可以使用func(slice...)来直接应用到变参函数中
  
	nums := []int{1, 2, 3, 4}
  
	sum(nums...)
  
}
  

Closures

Go 支持匿名函数,它可以形成闭包。当你想要定义一个不记名的内部函数时,匿名函数就很有用了。


package main
  
import "fmt"
  
//intSeq函数返回另一个函数,它定义在了intSeq函数内部,并且是匿名的。
  
//返回的函数关闭变量i以形成闭包
  
func intSeq() func int{
  
  i := 0
  
  return func() int{
  
    i+=1
  
    return i
  
  }
  
}
  
func main(){
  
  //调用intSeq,并将结果(一个函数)赋予nextInt
  
  //这个函数持有一个自己的i值,每次调用nextInt时都会更新
  
  nextInt:=intSeq()
  
  //多次调用nextInt函数可以看到闭包的效果
  
  //zephyr:如非闭包写法,每次函数都会进行初始化变量i,反复调用intSeq不会有这个效果
  
  fmt.Println(nextInt())
  
  fmt.Println(nextInt())
  
  fmt.Pritnln(nextInt())
  
  //要确认该状态对该特定函数是唯一的,请创建并测试一个新函数。
  
  newInts:=intSeq()
  
  fmt.Println(newInts())
  
}
  

Recursion

Go 支持递归函数。下面是一个经典的斐波那契数列示例


package main
  

  
import "fmt"
  

  
func fact(n int) int {
  
	if n == 0 {
  
		return 1
  
	}
  
	//fact函数调用自身,直到n为0
  
	return n * fact(n-1)
  
}
  
func main() {
  
	fmt.Println(fact(7))
  
}
  

Pointers

Go 可以使用指针,让你在程序中传递值或记录的引用。

下面通过两种方式的对比来展示指针的使用:zeroval 和 zeroptr


package main
  

  
import "fmt"
  

  
//zeroval将取得ival的值的拷贝,与调用方不同
  
func zeroval(ival int) {
  
	ival = 0
  
}
  

  
//zeroptr有一个*int类型的参数,代表它接收的是一个指针
  
func zeroptr(iptr *int) {
  
	//*iptr解引用,从内存指定地址中获取存放的值
  
	//对解引用指针的赋值将改变指定地址上的值
  
	*iptr = 0
  
}
  
func main() {
  
	i := 1
  
	fmt.Println("initial:", i)
  
	zeroval(i)
  
	fmt.Println("zeroval:", i)
  
	//&i 语法将获得变量i的内存地址,也就是指向变量i的指针
  
	zeroptr(&i)
  
	fmt.Println("zeroptr:", i)
  
	//指针也可以被打印
  
	fmt.Println("pointer:", &i)
  
}
  

Structs

Go 的 Struct 结构是字段类型的集合。对于从记录中将数据组织到一起很有帮助。


package main
  

  
import "fmt"
  

  
type person struct {
  
	name string
  
	age  int
  
}
  

  
func main() {
  
	//这个语法创建了一个新的struct
  
	fmt.Println(person{"Bob", 20})
  
	//在初始化struct时,可以指定字段名
  
	fmt.Println(person{name: "Alice", age: 30})
  
	//被忽略的字段将会被初始化为零
  
	fmt.Println(person{name: "Fred"})
  
	//一个&将产生struct的指针
  
	fmt.Println(&person{name: "Ann", age: 40})
  
	s := person{name: "Sean", age: 50}
  
	//通过点号访问结构体中的字段
  
	fmt.Println(s.name)
  
	sp := &s
  
	fmt.Println(sp.age)
  
	//对于结构体的指针也可以使用点号操作符,指针将会自动解引用
  
	sp.age = 51
  
	//结构体是可变的
  
	fmt.Println(sp.age)
  
}
  

Methods

Go 支持在结构体上定义方法。


package main
  

  
import "fmt"
  

  
type rect struct {
  
	width, height int
  
}
  

  
//area方法有一个rect指针类型的接收器
  
func (r *rect) area() int {
  
	return r.width * r.height
  
}
  

  
//即可以定义指针类型的接收器,也可以定义值类型的接收器
  
func (r rect) perim() int {
  
	return 2*r.width + 2*r.height
  
}
  
func main() {
  
	r := rect{width: 10, height: 5}
  
	//调用定义的方法
  
	fmt.Println("area: ", r.area())
  
	fmt.Println("perim: ", r.perim())
  
	//Go为方法调用自动处理了值和引用的转换。使用指针接收器可以避免获得方法调用的拷贝(?)或允许方法修改接收到的struct值
  
	rp := &r
  
	fmt.Println("area: ", rp.area())
  
	fmt.Println("perim: ", rp.perim())
  
}
  

  

Interfaces

接口是方法签名的命名集合。


package main
  

  
import "fmt"
  
import "math"
  

  
//这是一个geometry的基本接口,本例中将在rect类型和circle类型中实现这个接口
  
type geometry interface {
  
	area() float64
  
	perim() float64
  
}
  
type rect struct {
  
	width, height float64
  
}
  
type circle struct {
  
	radius float64
  
}
  

  
//在Go中,实现一个接口只需要实现其中定义的所有方法即可
  
func (r rect) area() float64 {
  
	return r.width * r.height
  
}
  
func (r rect) perim() float64 {
  
	return 2*r.width + 2*r.height
  
}
  
func (c circle) area() float64 {
  
	return math.Pi * c.radius * c.radius
  
}
  
func (c circle) perim() float64 {
  
	return 2 * math.Pi * c.radius
  
}
  

  
//如果变量是接口类型,那么它可以调用接口内定义的方法
  
//这是一个通用的measure方法,利用它能够工作在任何geometry上
  
func measure(g geometry) {
  
	fmt.Println(g)
  
	fmt.Println(g.area())
  
	fmt.Println(g.perim())
  
}
  
func main() {
  
	r := rect{width: 3, height: 4}
  
	c := circle{radius: 5}
  
	//rect和circle均实现了geometry接口,所以可以作为measure的参数
  
	measure(r)
  
	measure(c)
  
}
  

Errors


package main
  

  
import "errors"
  
import "fmt"
  

  
//按照惯例,错误是最后一个返回值,类型为error,一个内置的接口。
  
func f1(arg int) (int, error) {
  
	if arg == 42 {
  
		//errors.New使用给定的错误信息构建了一个基本的error值
  
		return -1, errors.New("can't work with 42")
  
	}
  
	//在error位置上放nil值代表没有错误
  
	return arg + 3, nil
  
}
  

  
//通过实现Error()方法,可以自定义错误类型。这里有一个上例的变种
  
//使用一个自定义类型来显式地展示一个参数错误
  
type argError struct {
  
	arg  int
  
	prob string
  
}
  
type argError struct {
  
	arg  int
  
	prob string
  
}
  

  
func (e *argError) Error() string {
  
	return fmt.Sprintf("%d - %s", e.arg, e.prob)
  
}
  
func f2(arg int) (int, error) {
  
	if arg == 42 {
  
		//这里我们使用&argError语法来创建一个新的结构体,提供了arg和prob两个域的值
  
		return -1, &argError{arg, "can't work with it"}
  
	}
  
	return arg + 3, nil
  
}
  
func main() {
  
	//下面的两个循环测试了每个返回error的函数
  
	//注意在if中的错误检查是Go代码中的习惯用法
  
	for _, i := range []int{7, 42} {
  
		if r, e := f1(i); e != nil {
  
			fmt.Println("f1 failed:", e)
  
		} else {
  
			fmt.Println("f1 worked:", r)
  
		}
  
	}
  
	for _, i := range []int{7, 42} {
  
		if r, e := f2(i); e != nil {
  
			fmt.Println("f1 failed:", e)
  
		} else {
  
			fmt.Println("f1 worked:", r)
  
		}
  
	}
  
	//如果要以编程方式使用自定义错误中的数据,
  
	//则需要通过类型断言将错误作为自定义错误类型的实例获取。
  
	_, e := f2(42)
  
	if ae, ok := e.(*argError); ok {
  
		fmt.Println(ae.arg)
  
		fmt.Println(ae.prob)
  
	}
  
}
  

  

Goroutines

goroutine 是一个轻量级的执行线程。


package main
  

  
import "fmt"
  

  
func f(from string) {
  
	for i := 0; i < 3; i++ {
  
		fmt.Println(from, ";", i)
  
	}
  
}
  
func main() {
  
	//假设我们有个f(s)的函数调用。这里我们通过一般方法调用,令其同步执行
  
	f("direct")
  
	//要想让这个函数在goroutine中触发,使用go f(s)。这个新的goroutine将会与调用它的并行执行
  
	go f("goroutine")
  
	//我们也可以启动一个调用匿名函数的goroutine
  
	go func(msg string) {
  
		fmt.Println(msg)
  
	}("going")
  
	//现在,这两个方法调用在独立的goroutine中异步执行了,故方法执行直接落到了这里
  
	//Scanln代码需要在程序退出前按下一个键
  
	var input string
  
	fmt.Scanln(&input)
  
	fmt.Println("done")
  
}
  

Channels

Channel 是连接并发执行的 goroutine 的管道。你可以从一个 goroutine 传递值到 channel 中,再在另一个 goroutine 接收它。


package main
  

  
import "fmt"
  

  
func main() {
  
	//通过make(chan val-type)创建新的channel
  
	//channel的类型依赖于它们要传递的值
  
	messages := make(chan string)
  
	//向channel传递值使用channel <- 语法。在这里我们从一个新的goroutine中发送了一个"ping"到message通道中
  
	go func() { messages <- "ping" }()
  
	//<-channel语法从channel中获取值。这里我们接收了上面发送的"ping"信息并打印
  
	msg := <-messages
  
	fmt.Println(msg)
  
}
  

Channel Buffering


package main
  

  
import "fmt"
  

  
func main() {
  
	//这里我们创建了一个能够缓冲2个字符串值的channel
  
	messages := make(chan string, 2)
  
	//由于channel带有缓冲区,我们可以发送值,无需响应的并发接收
  
	messages <- "buffered"
  
	messages <- "channel"
  
	//稍后,我们像往常一样,接收了这两个值
  
	fmt.Println(<-messages)
  
	fmt.Println(<-messages)
  
}
  

Channel Synchronization

我们可以使用 channel 来跨 goroutine 同步执行。这里是一个使用阻塞接收来等待 goroutine 结束的例子。


package main
  
import "fmt"
  
import "time"
  
//这个方法将在一个goroutine中运行。
  
//done channel用来通知其他的goroutine这个方法执行完毕
  
func worker(done chan bool){
  
  fmt.Print("working...")
  
  time.Sleep(time.Second)
  
  fmt.Println("done")
  
  //发送一个值来通知这里已经做完
  
  done<-true
  
}
  
func main(){
  
  启动一个worker goroutine,赋予它用以通知的channel
  
  done:=make(chan bool,1)
  
  go worker(done)
  
  //在channel接收到来自worker的通知前,保持阻塞
  
  <-done
  
}
  

Channel Directions

当把 channel 用作函数的参数时,你可以指定一个 channel 是否只发送或者只接收数据。这种特异性增加了程序的类型安全性。


package main
  

  
import "fmt"
  

  
//ping函数只接受一个发送数据的channel,如果试图在其上获取数据,将会引发编译时异常
  
func ping(pings chan<- string, msg string) {
  
	pings <- msg
  
}
  

  
//pong函数接受一个通道用于接收(pings),另一个用于发送(pongs)
  
func pong(pings <-chan string, pongs chan<- string) {
  
	msg := <-pings
  
	pongs <- msg
  
}
  
func main() {
  
	pings := make(chan string, 1)
  
	pongs := make(chan string, 1)
  
	ping(pings, "passed message")
  
	pong(pings, pongs)
  
	fmt.Println(<-pongs)
  
}
  

Select

Go 的 select 让你能够等待多个 channel 操作。通过 select 结合 goroutine 和 channel 是 Go 的重要特色。


package main
  

  
import "time"
  
import "fmt"
  

  
func main() {
  
	//本例中我们将在两个通道中进行选择
  
	c1 := make(chan string)
  
	c2 := make(chan string)
  
	//每个通道都会在一定时间后接收到一个值,在并发的goroutine中模拟阻塞RPC操作执行
  
	go func() {
  
		time.Sleep(time.Second * 1)
  
		c1 <- "one"
  
	}()
  
	go func() {
  
		time.Sleep(time.Second * 2)
  
		c2 <- "two"
  
	}()
  
	//我们将使用select来同时等待这两个值,当它们到达时打印。
  
	for i := 0; i < 2; i++ {
  
		select {
  
		case msg1 := <-c1:
  
			fmt.Println("received", msg1)
  
		case msg2 := <-c2:
  
			fmt.Println("received", msg2)
  
		}
  
	}
  
}
  

Timers

我们常想在未来的某一刻执行 Go 代码,或者在某一时间内重复执行。Go 内置的 timer 和 ticker 使这些任务十分简易。首先我们看看 timer,然后再看 ticker。


package main
  

  
import "time"
  
import "fmt"
  

  
func main() {
  
	//timer代表未来的一个单独事件。你要告诉它要等待多久,它提供一个通道,在指定时间发出通知。下面这个timer将等待2秒钟
  
	timer1 := time.NewTimer(time.Second * 2)
  
	//定时器通道由于操作<-timer1.c发生阻塞,直到它发送了一个值来表明定时器到时
  
	<-timer1.C
  
	fmt.Println("Timer 1 expired")
  
	//如果你仅仅想等待一段时间,可以用time.Sleep,使用timer的一个原因是,你可以在计时结束前取消,如下例:
  
	timer2 := time.NewTimer(time.Second)
  
	go func() {
  
		<-timer2.C
  
		fmt.Println("Timer 2 expired")
  
	}()
  
	stop2 := timer2.Stop()
  
	if stop2 {
  
		fmt.Println("Timer 2 stopped")
  
	}
  
}
  

Tickers

timer 用来在将来的某一时间做某事一次。而 ticker 会在一个指定时间间隔重复做某事。这里是一个 ticker 的例子,它会在我们停止之前定期触发


package main
  

  
import "time"
  
import "fmt"
  

  
func main() {
  
	//ticker与timer的机制相似,都是一个发送值的通道
  
	//这里我们使用channel内置的range来遍历每500ms到达的值
  
	ticker := time.NewTicker(time.Millisecond * 500)
  
	go func() {
  
		for t := range ticker.C {
  
			fmt.Prinltn("Tick at ", t)
  
		}
  
	}()
  
	//ticker可以像timer一样停止。一旦ticker停止,将不会在其通道上接收到任何信息。我们将在1600ms后结束。
  
	time.Sleep(time.Millisecond * 1600)
  
	ticker.Stop()
  
	fmt.Println("Ticker stopped")
  
}
  

Defer

defer 用于确保一个函数调用在程序执行中延迟作用,经常用于清理目的。defer 常用语其他语言的 ensure 和 finnaly 用的地方。


packgae main
  
import "fmt"
  
import "os"
  
//假设我们想要创建并写一个文件,在完成后关闭它。
  
func main(){
  
  //在获取一个文件对象后立即使用defer并关闭这个文件
  
  //这个将在main函数末尾关闭的时候执行,在writeFile完成后
  
  f:=createFile("/tmp/defer.txt")
  
  defer closeFile(f)
  
  writeFile(f)
  
}
  
func createFile(p string) *os.File{
  
  fmt.Println("creating")
  
  f,err:=os.Create(p)
  
  if err!=nil{
  
    panic(err)
  
  }
  
  return f
  
}
  
func writeFile(f *os.File){
  
  fmt.Println("writing")
  
  fmt.Println(f,"data")
  
}
  
func closeFile(f *os.File){
  
  fmt.Println("closing")
  
  f.Close()
  
}
  

Regular Expressions


package main
  

  
import "bytes"
  
import "fmt"
  
import "regexp"
  

  
func main() {
  
	//测试模式是否符合字符串
  
	math, _ := regexp.MatchString("p([a-z]+)ch", "peach")
  
	fmt.Printf(match)
  
	//上面我们直接使用了字符串模式。但是对于其他正则任务,你需要先编译一个正则表达式结构体
  
	r, _ := regexp.Compile("p([a-z]+)ch")
  
	//这种结构体上有许多方法。这里是一个如同上面的匹配测试
  
	fmt.Println(r.MatchString("peach"))
  
	//这里找到一个匹配
  
	fmt.Println(r.FindString("peach punch"))
  
	//这里也是寻找第一个匹配,但返回起止的索引而非字符串
  
	fmt.Println(r.FindStringIndex("peach punch"))
  
	//submatch包含整串匹配,也包含内部的匹配。
  
	fmt.Println(r.FindStringSubmatch("peach punch"))
  
	//与上面类似,返回整串匹配和内部匹配的索引信息
  
	fmt.Println(r.FindStringSubmatchIndex("peach punch"))
  
	//这些函数的All修饰将返回输入中所有匹配的,不仅仅是第一个。例如找到所有匹配正则表达式的
  
	fmt.Println(r.FindAllString("peach punch pinch", -1))
  
	fmt.Println(r.FindAllStringSubmatchIndex("peach punch pinch", -1))
  
	//第二个参数如果是非负数,则将限制最多匹配的个数
  
	fmt.Println(r.FindAllString("peach punch pinch", 2))
  
	//例子中都是字符串参数,并且用了类似于MatchString这样的名字
  
	//我们可以提供[]byte参数,并将函数名中的String去掉
  
	fmt.Println(r.Match([]byte("peach")))
  
	//当用正则表达式创建一个常量的时候你可以使用MustCompile。
  
	//一个纯Compile不能用于常量,因为它有2个返回值。
  
	r = regexp.MustComplie("p([a-z]+)ch")
  
	fmt.Println(r)
  
	//regexp包也能用于使用其他值替换字符串的子集
  
	fmt.Println(r.ReplaceAllString("a peach", "<fruit>"))
  
	//Func修饰允许你使用一个给定的函数修改匹配的字符串
  
	in := []byte("a peach")
  
	out := r.ReplaceAllFunc(in, bytes.ToUpper)
  
	fmt.Println(string(out))
  
}
  

分类列表:


  1. arrays
  2. change
  3. channel
  4. declarations
  5. embedding
  6. file
  7. for
  8. fyne
  9. gin
 10. glamour
 11. go
 12. hello
 13. if
 14. interface
 15. lorca
 16. map
 17. markdown
 18. Operator
 19. pointers
 20. print
 21. regexp
 22. slices
 23. string
 24. struct
 25. switch
 26. time
 27. types
 28. vendor

腾图小抄 SCWY.net v0.03 小抄561条 自2022-01-02访问317546次