Go

  • 一,惊鸿一瞥
    • 1,Hello,World
    • 2,基本知识
    • 3,基本控制结构
    • 4,作用域
  • 二,类型
    • 1,浮点数
    • 2,整数
    • 3,big包
    • 4,多语言文本
    • 5,类型转换
  • 三,构建块
    • 1,函数
    • 2,方法
    • 3,一等函数
  • 四,收集器
    • 1,数组
    • 2,切片
    • 3,映射
  • 五,状态与行为
    • 1,结构
    • 2,Go面向对象
    • 3,接口
  • 六,指针
    • 1,基本使用
    • 2,nil
  • 七,异常处理
  • 八,并发
    • 1,goroutine
    • 2,通道
    • 3,Goroutine池
    • 4,互斥锁

一,惊鸿一瞥

1,Hello,World

package mainimport {"fmt"
}func main() {fmt.Println("Hello, World!")
}

package关键字声明代码所属的包,本例中包名为main。
import关键字导入需要的包
func关键字声明函数
当运行一个Go程序,总是从main包的main函数开始

fmt包:format之缩写,提供格式化输入输出函数

此外,大括号有其唯一的制定格式

2,基本知识

package mainimport "fmt"func main() {//声明常量const days = 365//声明变量var (dream = "I have a dream"new_dream = "I had a dream")//格式化输出fmt.Println("One year has",days,"days")fmt.Print("One year has ")fmt.Print(days)fmt.Print(" days\n")fmt.Printf("One year has %d days\n",days)fmt.Println(dream,"\n",new_dream)
}

Println会自动补换行符

Go中运算符和C中大抵相同,但没有++count这种前置增量操作

3,基本控制结构

在go中,true是唯一真值,false是唯一假值

比较运算符:==,!=,<=,<,>=,>

Go不允许文本和数值直接比较

func demo(){if 判断语句{//do sth.}else if 判断语句{//do sth.}else 判断语句{//do sth.}

逻辑与:&&
逻辑或:||
逻辑非:!

Switch实现分支判断:

package mainimport "fmt"func main() {const days = 365switch days {case 100:fmt.Println(100,"days")case 200,365://使用逗号分隔多个可选值fmt.Println("hhhh")fallthroughdefault:fmt.Println("23333333")}
}

for实现循环结构
关于range关键字:

//假设array_cut是一个数组或切片
//index和value分别对应了下标和值
for index,value := range array_cut{//do sth.
}

4,作用域

go作用域:以大括号分界
虽然没有用到大括号,但default和case都i引入了新的作用域

简短声明:

//两方法等价
var name = 'ubuntu'
name := 'ubuntu'

简短声明作用:
以下三种情况,变量作用域都在大括号内
这种使用方法无法用var声明替代

for count := 10; count > 0;count--{//do sth.
}if count := rand.Intn(3); count == 0{//do sth.
}switch num := rand(10); num{//do sth.
}

二,类型

1,浮点数

//双精度浮点数
age := 19.2
var age =19.2
var age float64 = 19.2//单精度浮点数
var age float32 = 19.2//默认零值
var age

2,整数

year := 2021
var year = 2021
var year int 2021//e12表示10的12次方
var distance int64  = 2.33e12

3,big包

big包提供3种类型:

  • 存储大整数big.Int
  • 存储任意精度浮点数big.Float
  • 存储分数big.Rat
//创建int64类型
hhhhh := big.NewInt(2333333)//创建很大很大的数
hhhhhhh := new(big.Int)
hhhhhhh.SetString("23333333333333333",10)//Go语言不会为常量推断类型,而是默认无类型(untyped)
//由big包提供底层支持
const distance = 23333333333333333333333333

4,多语言文本

//go语言推断双引号内字面值为string类型
peace := "peace"
var peace = "peace"
var peace string = "peace"//声明零值
var peace string//反引号包围的文本不考虑转义字符
var peace 'hhhhhhh\n'//不会输出换行符,而是输出/n//反引号可跨越多行,打印时将包含这些行
var peace 'hhhhh
hhhhhhhh
hhhhh'
//可以独立访问字符串中的单个字符,但无法单独修改
hhhh = 'laugh'
h = laugh[4]//非法操作:
laugh[4] = 'd'

5,类型转换

类型转换:
转换一个变量,只需用与目标类型同名的函数来包裹该变量

//字符串拼接
hhhh = 'laugh ' + 'is the power of birth.'//非法:
hhh ='laugh ' + 'is the power of birth.' + 10//合法
hhh = 'laugh ' + 'is the power of birth.' + '10'

这部分与c语言大致相同
浮点数转换为整数,没有任何舍入,而是直接截断

关于字符串转换

//根据代码点转换
fmt.Println(string(960),string(940),string(969),string(33))
//输出π ά ω !//转换为字符串方法:
//使用strconv包的Itoa函数
hhh ='laugh ' + 'is the power of birth.' + strconv.Itoa(10)//字符串转换为整数
//使用strconv包的Atoi函数
const hhh string = '23333'
number := strconv.Atoi(hhh)

代码点:
一个数字对应一个特殊字符
如:π ά ω !分别对应960 940 969 33

三,构建块

1,函数

基本格式:

func function_name( [parameter list] ) [return_types] { 函数体 }

func Add(number1 int,number2 int)(sum int){sum = number1 + number2return sum
}func Add(number1 int,number2 int)(sum int){sum = number1 + number2return //省略了返回值的名字
}func Calculate(number1 ,number2 int)(sum,multitude int){//减少了int的使用次数sum = number1 + number2multitude = number2*number1return sum,multitude
}func Calculate(number1 ,number2 int)(sum,multitude int){sum = number1 + number2multitude = number2*number1return

2,方法

声明新类型:
格式:

type 新类型名称 底层类型

注意:

type type1 float64
type type2 float64var number1 type1 := 10
var number2 type2 := 15
var number3 float64 = 20//以下操作非法.尽管底层相同,他们仍为不同的类型
numebr1 = number2
number2 = number3

在go语言中,没有类和对象
格式:

func (接受者) 方法名(形参列表) (返回值列表){//do sth.
}

接受者是某种非内置类型的变量
用于建立类型和方法之间的关联

实例:

package mainimport "fmt"type myInt intfunc (number1 myInt) calculate(number2 int) (sum int,multitude int){return int(number1)+number2,int(number1)*number2
}func main() {var number1 myInt = 10var number2 =20var sum intvar multitude intsum,multitude = number1.calculate(number2)fmt.Println(sum,multitude)
}

3,一等函数

在go中,函数是 一等 值,可以把函数赋值给变量,可以把函数传递给函数,也可以编写创建并返回函数的函数。

将函数赋值给变量

package mainimport "fmt"type myInt int64func Add(number1,number2 int) myInt {return myInt(number1+number2)
}func Multitude(number1,number2 int) myInt{return myInt(number1*number2)
}func main() {//将函数赋值给变量方法一:func1 := Add//将函数赋值给变量方法二var func2 func(int,int) myInt = Multitudevar number1 int = 10var number2 int = 20var result myIntresult = func1(number1,number2)fmt.Println(result)result = func2(number1,number2)fmt.Println(result)func1 = func2result = func1(number1,number2)fmt.Println(result)func2 = Addresult = func2(number1,number2)fmt.Println(result)
}

将函数传递给其他函数

type myInt int64
//声明函数类型
type myFunc func(int,int) myIntfunc Add(number1,number2 int) myInt {return myInt(number1+number2)
}func Multitude(number1,number2 int) myInt{return myInt(number1*number2)
}func Add_Multitude(number1,number2 int, func1,func2 myFunc) (sum,multitude myInt){sum = func1(number1,number2)multitude = func2(number1,number2)return sum,multitude
}

闭包和匿名函数
匿名函数在Go中也称为函数字面量

闭包函数:声明在一个函数内部的函数
闭包:内部函数总是可以访问其所在外部函数中的变量,即使它所在的外部函数已返回(寿命终结)

package mainimport "fmt"type myFunc func() intfunc func1(func2 myFunc,offset int) myFunc{return func() int {return func2() + offset}
}func func2() int{return 10
}func main() {//匿名函数赋值给变量func4 := func(message string){fmt.Println(message)}func4("hhhhhhhhhhhhhhhhhhhhhhh")//同时声明和调用匿名函数func(){fmt.Println("hhhhhhhhh")} ()//从函数里返回另一个函数func3 := func1(func2,20)fmt.Println(func3())
}

可变参数函数

声明放在函数形参的最后一位
可以传入多个参数或不传参数

func f(name...string) {//do sth.
}cut_test := []string{"sss","hhh","2333"}
//将cut_test的多个元素传递给函数
cut = f(cut_test..)

四,收集器

1,数组

var planests [8]stringplanest[0] = "Mercury"
mercury := planets[0]
fmt.Println(len(planest))//输出8//未赋值的元素将默认为零值
var numbers = [5]int{1,2,3}//也可以不用指定数组长度
var numbers = [...]int{1,
2,
3,
}//结尾逗号是必需的

数组用于赋值或传递给参数时会产生一个副本
故函数内部对数组的修改不会影响原数组

数组嵌套定义即形成所谓的多维数组

2,切片

切片数组不会导致数组被修改,只是创建了指向数组的视图,把该视图称为切片类型

格外注意的是,切片或切片的切片都是数组的视图,对他们进行修改会导致原数组的修改

package mainimport ("fmt""sort"
)func main() {numbers := [5]int{1,2,3,4,5}cut1 := numbers[0:3]cut2 := numbers[0:2]fmt.Println(numbers,cut1,cut2)cut2[1] = 100fmt.Println(numbers)//可以切割string类型,将创建另一个字符串,两个字符串相互独立name  := "yangzi"name1 := name[:4]fmt.Println(name,name1)name = "hhhhh"fmt.Println(name,name1)//创建完全切片方法一://numbers2 := numebrs[:]//创建完全切片方法二:numbers3 := []string{"11","40","ann","ccc","bbbb"}//切片可以带有方法sort.StringSlice(numbers3).Sort()fmt.Println(numbers3)
}

关于sort:
标准库sort包声明了一种StringSlice类型:

type StringSlice []string

该类型还有关联的Sort方法:

func (p StringSlice) Sort()

Go语言通过切片和append函数来实现这一点

切片长度:切片中可见元素的数量
切片容量:切片的底层数组决定了切片的容量

package mainimport "fmt"func main(){//切片的底层数组长度为5//切片长度为5,容量为5vocabulary := []string{"terminally","visible","lethal","legally","prescribe"}//此时,append函数将切片元素复制到新创建的数组里面并将新创建的数组作为切片的底层数组,该数组长度为原底层数组的两倍//切片长度为8,容量为10vocabulary = append(vocabulary,"partnership","compassion","publication")//三索引切片操作://三索引操作旨在避免对底层数组的修改arry_vocabulary := [...]string{"candidness","dose","medication","constitutionally","uphold","legislation",}//切片长度为4,容量为4cut_vocabulary := arry_vocabulary[0:4:4]fmt.Println(cut_vocabulary)cut_vocabulary = append(cut_vocabulary,"spark")fmt.Println(cut_vocabulary)fmt.Println(arry_vocabulary)//两索引//长度为2,容量为6(与底层数组相同)cut_vocabulary2 := arry_vocabulary[0:4]cut_vocabulary2 = append(cut_vocabulary,"notable")fmt.Println(cut_vocabulary2)fmt.Println(arry_vocabulary)//三索引但容量大于长度cut_vocabulary3 := arry_vocabulary[0:4:5]cut_vocabulary3 = append(cut_vocabulary,"spark")fmt.Println(cut_vocabulary3)fmt.Printpackage mainimport "fmt"func main(){//声明map方法一:map1 := map[string]int{"key1":1,"key2":2,}//声明map方法二map2 := make(map[string]int)map2["New_York"] = 1map2["San_Franscisco"] = 2map2["Tokyo"] = 3//数组或一些基本类型在赋值给新变量或传递至函数/方法时都会创建相应的副本,但映射不会map3 := map1map3["key1"]=2fmt.Println(map3)fmt.Println(map1)//遍历映射//遍历键for key := range map2{fmt.Print(key)}fmt.Print("\n")//遍历值for _,value := range map2{fmt.Print(value)}fmt.Print("\n")//遍历键值for key,value := range map2{fmt.Println(key,value)}//删除键值对//delete(map_name,key_name)delete(map2,"New_York")//判断某一个键值对是否存在//当键值对不存在,输出对应数据类型的零值fmt.Println(map2["New_York"])//使用value, ok := map[key]的写法判断map2["Shenzheng"] = 0if value,ok := map2["Shenzheng"]; ok{fmt.Println(value," is exited.")}else {fmt.Println("not exited")}
}ln(arry_vocabulary)//使用make函数对切片容量实行预分配//切片长度为0,容量为10test_cut1 := make([]string,0,10)test_cut1 = append(test_cut1,"hhhhhhhhhhhhhh")//切片长度为10,容量为10,元素默认为零值test_cut2 := make([]string,10)//此时将向切片中追加第11个元素test_cut2 = append(test_cut2,"hhhhhhhhh")
}

3,映射

映射(map):可将键映射到值
与数组和切片使用序列整数作为索引不同的是,映射的键几乎可以是任何类型

可以理解为Python中的字典

Go语言必须为映射的键和值指定类型
格式:

map[键的类型]值的类型

示例:

package mainimport "fmt"func main(){//声明map方法一:map1 := map[string]int{"key1":1,"key2":2,}//声明map方法二map2 := make(map[string]int)map2["New_York"] = 1map2["San_Franscisco"] = 2map2["Tokyo"] = 3//数组或一些基本类型在赋值给新变量或传递至函数/方法时都会创建相应的副本,但映射不会map3 := map1map3["key1"]=2fmt.Println(map3)fmt.Println(map1)//遍历映射//遍历键for key := range map2{fmt.Print(key)}fmt.Print("\n")//遍历值for _,value := range map2{fmt.Print(value)}fmt.Print("\n")//遍历键值for key,value := range map2{fmt.Println(key,value)}//删除键值对//delete(map_name,key_name)delete(map2,"New_York")//判断某一个键值对是否存在//当键值对不存在,输出对应数据类型的零值fmt.Println(map2["New_York"])//使用value, ok := map[key]的写法判断map2["Shenzheng"] = 0if value,ok := map2["Shenzheng"]; ok{fmt.Println(value," is exited.")}else {fmt.Println("not exited")}
}

五,状态与行为

状态:可以是值或结构package main
行为:函数和方法

1,结构

基本用法

package mainimport "fmt"//定义结构
type city struct{country,name stringrank int
}func main(){//声明方法一:var newYork citynewYork.rank = 1newYork.name = "New York"newYork.country = "American"//声明方法二:var tokyo = city{country:"Japan",rank:2}//未给定字段则自动声明import "fmt"//定义结构
type city struct{country,name stringrank int
}func main(){//声明方法一:var newYork citynewYork.rank = 1newYork.name = "New York"newYork.country = "American"//声明方法二:var tokyo = city{country:"Japan",rank:2}//未给定字段则自动声明为对应的零值fmt.Println(tokyo.name)tokyo.name = "Tokyo"//结构复制//两个结构变化不会对彼此有任何影响anotherTokyo := tokyoanotherTokyo.rank = 3fmt.Println(anotherTokyo,"\n",tokyo)//由结构组成的切片cities := []city{newYork,tokyo}fmt.Println(cities)//临时使用的匿名结构hhhh := struct {ccc intddd string}{2333333,"2333333333"}fmt.Println(hhhh)
}
为对应的零值fmt.Println(tokyo.name)tokyo.name = "Tokyo"//结构复制//两个结构变化不会对彼此有任何影响anotherTokyo := tokyoanotherTokyo.rank = 3fmt.Println(anotherTokyo,"\n",tokyo)//由结构组成的切片cities := []city{newYork,tokyo}fmt.Println(cities)//临时使用的匿名结构hhhh := struct {ccc intddd string}{2333333,"2333333333"}fmt.Println(hhhh)
}

2,Go面向对象

Go没有类,而是通过结构与方法的结合实现面向对象

type literallyPopulation struct {literalRate float64totalPopulation int
}type country struct {countryName string//运用组合theLiterallyPopulation literallyPopulation
}//实现构造函数
func newCountry(countryName string, literalRate float64,totalPopulation int) (c country){l := literallyPopulation{literalRate:literalRate,totalPopulation:totalPopulation}c = country{countryName:countryName,theLiterallyPopulation: l}return c
}func (theLiteralPopulation literallyPopulation) countLiteralPopulation() (float64){return theLiteralPopulation.literalRate*float64(theLiteralPopulation.totalPopulation)
}//转发
//手动转发
func (c country) countLiteralPopulation() float64{return c.theLiterallyPopulation.literalRate*float64(c.theLiterallyPopulation.totalPopulation)
}

实现自动转发:

ckage mainimport "fmt"type literallyPopulation struct {literalRate float64totalPopulation int
}type country struct {countryName string//运用组合literallyPopulation
}//实现构造函数
func newCountry(countryName string, literalRate float64,totalPopulation int) (c country){l := literallyPopulation{literalRate:literalRate,totalPopulation:totalPopulation}c = country{countryName:countryName,literallyPopulation:l}ar slice []int //值为nil的切片//关键字range,len()和append()等可以正常处理nil切片for _, ingredient := range slice {fmt.Println(ingredient) //不会包含任何输出}fmt.Println(len(slice))slice = append(slice, 1, 2, 3)fmt.Println(slice)return c
}func (theLiteralPopulation literallyPopulation) countLiteralPopulation() (float64){return theLiteralPopulation.literalRate*float64(theLiteralPopulation.totalPopulation)
}func main(){c := newCountry("hhhh",0.34,10000)
lp := c.countLiteralPopulation()
fmt.Println(lp)
}

3,接口

接口是一种类型,是一组method的集合
接口命名一般以-er结尾

package mainimport "fmt"type shower interface {showName() stringshowRank() int
}type sayer interface {sayCharacteristic() string
}//接口嵌套
type sayAndShower interface {sayershower
}type city struct {name           stringrank           intcharacteristic string
}type country struct {name           stringrank           intcharacteristic string
}func (city city) showName() string {return city.name
}func (city city) showRank() int {return city.rank
}func (city city) sayCharacteristic() string {return city.characteristic
}func (country country) showName() string {return country.name
}func (country country) showRank() int {return country.rank
}func (country country) sayCharacteristic() string {return country.characteristic
}func main() {structCity := city{characteristic: "very big", rank: 23333, name: "city of night"}structCountry := country{characteristic: "nb", rank: 100, name: "nbcountry"}var show showervar say sayershow = structCityfmt.Println(show.showRank())fmt.Println(show.showName())show = structCountryfmt.Println(show.showRank())fmt.Println(show.showName())say = structCityfmt.Println(say.sayCharacteristic())say = structCountryfmt.Println(say.sayCharacteristic())var showAndSay sayAndShowershowAndSay = structCountryfmt.Println(showAndSay.showRank(), showAndSay.showName(), showAndSay.sayCharacteristic())
}

六,指针

1,基本使用

几点需要注意的:字

  • 对于指向结构的指针,Go中用.即可完成对方法和字段的访问,且解引用不是必须的
  • Go中数组与指针是两种独立的类型
  • 对于指向数组的指针,Go语言提供了自动解引用的特性,即解引用不是必须的
  • Go语言没有为切片和映射提供了自动解引用的特性
  • 映射是一种隐式指针,所以是指针指向映射的举动往往是多此一举
  • 切片实际上是指向数组的指针
  • 指向切片的指针的作用是修改切片本身,包括切片的长度,容量和起始偏移量
  • 方法的接收者和函数形参在处理指针方面十分类似,都可以实现对原有字段的改变
  • 关于指针和接口:指针接收者能仅能接收指针变量,而值接收者可接受指针或非指针变量

2,nil

nil是指针,切片,映射和接口的零值
解引用nil指针将引发恐慌(panic)

nil与方法:

package maintype person struct {score int
}func (p *person) getScore() {p.score++ //此处发生了解引用
}func main() {var p *personp.getScore() //解引用nil指针,引发恐慌
}

nil与函数:

var f func()//当变量被声明为func()类型,其默认值为nil,f未被赋予任何函数
f()//尝试调用f(),引发惊恐
```package mainimport ("fmt"
)//空接口妙用之一:以空接口作为函数行参,可以向函数传递任何类型的值
func show(x interface{}){fmt.Printf("Type:%T Value:%v \n",x,x)
}func main(){//空接口可以储存任意类型的值//定义一个空接口的值var x interface{}i:= 100j:= "hhhhhh"k:= truex = ifmt.Printf("Type:%T Value:%v \n",x,x)x=jfmt.Printf("Type:%T Value:%v \n",x,x)x=kfmt.Printf("Type:%T Value:%v \n",x,x)show(i)show(j)show(k)//空接口妙用之二:使用空接口实现保存任何值的特点var stuInfo = make(map[string]interface{})stuInfo["name"] = "bai"stuInfo["age"] = 12stuInfo["sex"] = "nan"
}nil与切片:
不包含任何元素的空切片和nil切片并不相等,但通常可以替换使用```go
package mainimport "fmt"func main() {var slice []int //值为nil的切片//关键字range,len()和append()等可以正常处理nil切片for _, ingredient := range slice {fmt.Println(ingredient) //不会包含任何输出}fmt.Println(len(slice))slice = append(slice, 1, 2, 3)fmt.Println(slice)//关键字range,len()和append()等可以正常处理nil切片for _, ingredient := range slice {fmt.Println(ingredient) //不会包含任何输出}fmt.Printl//关键字range,len()和append()等可以正常处理nil切片for _, ingredient := range slice {fmt.Println(ingredient) //不会包含任何输出package mainimport ("fmt"
)//空接口妙用之一:以空接口作为函数行参,可以向函数传递任何类型的值
func show(x interface{}){fmt.Printf("Type:%T Value:%v \n",x,x)
}func main(){//空接口可以储存任意类型的值//定义一个空接口的值var x interface{}i:= 100j:= "hhhhhh"k:= truex = ifmt.Printf("Type:%T Value:%v \n",x,x)x=jfmt.Printf("Type:%T Value:%v \n",x,x)x=kfmt.Printf("Type:%T Value:%v \n",x,x)show(i)show(j)show(k)//空接口妙用之二:使用空接口实现保存任何值的特点var stuInfo = make(map[string]interface{})stuInfo["name"] = "bai"stuInfo["age"] = 12stuInfo["sex"] = "nan"
}}fmt.Println(len(slice))slice = append(slice, 1, 2, 3)fmt.Println(slice)n(len(slice))//不包含任何元素的空切片和nil切片替换使用slice = append(slice, 1, 2, 3)fmt.Println(slice)
}

nil与映射
映射在声明之后若没有使用复合字面量或内置make函数初始化,则其值为默认的nil。
可以对值为nil的map进行读取操作,但不可进行写入操作

package mainimport "fmt"func main() {var map1 map[string]int//进行读取操作value, ok := map1["hhhh"]if ok {fmt.Println(value)}for key, value := range map1 {fmt.Println(key, value)}
}

nil与接口

package mainimport ("fmt"
)//空接口妙用之一:以空接口作为函数行参,可以向函数传递任何类型的值
func show(x interface{}){fmt.Printf("Type:%T Value:%v \n",x,x)
}func main(){//空接口可以储存任意类型的值//定义一个空接口的值var x interface{}i:= 100j:= "hhhhhh"k:= truex = ifmt.Printf("Type:%T Value:%v \n",x,x)x=jfmt.Printf("Type:%T Value:%v \n",x,x)x=kfmt.Printf("Type:%T Value:%v \n",x,x)show(i)show(j)show(k)//空接口妙用之二:使用空接口实现保存任何值的特点var stuInfo = make(map[string]interface{})stuInfo["name"] = "bai"stuInfo["age"] = 12stuInfo["sex"] = "nan"
}

七,异常处理

go异常处理思想:
若一个函数可能出现异常,那么就应把异常作为返回值,没有异常就返回nil

关键字panic:抛出异常
关键字recover():捕获异常
关键字defer:延时操作,让操作在函数返回之前触发。出现在关键字defer之后的每return语句都会让defer后的操作触发

package mainimport ("errors"
)const rows, columns = 9, 9type Grid [rows][columns]int//errors包包含一个构造函数,接受一个代表错误信息的字符串作为参数
//声明错误变量
var (ErrBounds = errors.New("out of bounds")ErrDigit = errors.New("invalid digit")
)func (g *Grid) Set(row, column, digit int) error {if !inBounds(row,column){return ErrBounds}if !ifValid(digit){return ErrDigit}g[row-1][columns-1] = digitreturn nil
}func inBounds(row,column int) bool{if row < 1 || row > 9{return false}if column < 1 || column > 9{return false}return true
}func ifValid(digit int) bool{if digit < 1 || digit > 9{return false}return true
}

处理多个错误:
上述例子仅处理可能的一种错误,有时,我们要求返回多个可能的错误

//errors包中的error是一个内置的接口
//无论哪种类型,只要实现了符合条件的Error方法,那么他就满足了该接口
type error interface {Error() string
}

类型断言:
写法:

value,ok := em.(T)
element:要断言的变量,只能为接口类型
T:要断言的类型
value:变量的值
package mainimport ("errors""fmt""strings"
)const rows, columns = 9, 9type Grid [rows][columns]int//创建一个由error值组成的切片
type SudokuError []error//errors包包含一个构造函数,接受一个代表错误信息的字符串作为参数
//声明错误变量
var (ErrBounds = errors.New("out of bounds")ErrDigit  = errors.New("invalid digit")
)func (se SudokuError) Error() string {var s []stringfor _, err := range se {//这个Error到底是啥//这一步把错误转化为字符串s = append(s, err.Error())}return strings.Join(s, ",")
}func (g *Grid) Set(row, column, digit int) error {var errs SudokuErrorif !inBounds(row, column) {errs = append(errs, ErrBounds)}if !ifValid(digit) {errs = append(errs, ErrDigit)}if len(errs) > 0 {return errs}g[row-1][columns-1] = digitreturn nil
}func inBounds(row, column int) bool {if row < 1 || row > 9 {return false}if column < 1 || column > 9 {return false}return true
}func ifValid(digit int) bool {if digit < 1 || digit > 9 {return false}return true
}func main() {var g Griderr := g.Set(10, 2, 100)if value, ok := err.(SudokuError); ok {//会输出多个错误fmt.Println(value)}
}

处理惊恐

 //defer使相关操作在函数返回前进行defer func() {//判断是否接受到了惊恐if e := recover(); e != nil {//处理惊恐的步骤fmt.Println("23333333")}}()//抛出惊恐panic("hhhh")

八,并发

Go中,独立运行的任务谓之goroutine
goroutine之间的通信依赖通道
通道(channel)是一种类型

A.进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。
B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
C.一个进程可以创建和撤销多个线程;同一个进程中的多个线程之间可以并发执行。

并发:多线程程序在一个核的cpu上运行
并行:多线程程序在多个核的cpu上运行

1,goroutine

关于goroutine的实例一:

package mainimport ("fmt""sync"
)var wg sync.WaitGroupfunc sleepyGopher(i int) {defer wg.Done()fmt.Println(i, "awake")
}
package mainimport ("fmt""time"
)func write(chan1 chan string) {for {select {case chan1 <- "hhhhh":fmt.Println("write")default:fmt.Println("full")close(chan1)}time.Sleep(500 * time.Millisecond)}
}func main() {chan1 := make(chan string, 10)go write(chan1)for s:= range chan1{fmt.Println(s)}
}
func main() {for i := 0; i < 10; i++ {wg.Add(1)//使用go关键字启动一个线程go sleepyGopher(i)}wg.Wait()
}

关于sync.WaitGroup:
sync.WaitGroup

2,通道

通道:varName := make(chan typeName,capacity)
capacity是可选参数,用于设定缓冲容量

关于关闭通道:

1.对一个关闭的通道再发送值就会导致panic。
2.对一个关闭的通道进行接收会一直获取值直到通道为空。
3.对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。
4.关闭一个已经关闭的通道会导致panic。

阻塞:goroutine等待通道发送或接收操作的过程,被阻塞的goroutine不消耗任何资源
死锁:一个goroutine永远都等不到解除阻塞的事件的发生

死锁示例:

func main() {c:=make(chan int)<-c
}

select用于处理多个通道
select模板:

select {case <-chan1:// 如果chan1成功读到数据,则进行该case处理语句case chan2 <- 1:// 如果成功向chan2写入数据,则进行该case处理语句default:// 如果上面都没有成功,则进入default处理流程}

若启动多个goroutine且准备让他们无限期运行,那么可以用不包含任何分支的select来实现

通道示例:

package mainimport ("fmt""time"
)func write(chan1 chan string) {for {//判断通道缓存是否满了select {case chan1 <- "hhhhh":fmt.Println("write")default:fmt.Println("full")//通道第一次满之后,关闭通道close(chan1)return}time.Sleep(500 * time.Millisecond)}
}func main() {chan1 := make(chan string, 10)//创建一个线程,该线程与main线程是平行关系go write(chan1)//从通道里读值,直到他被关闭为止,方法一for s := range chan1 {fmt.Println(s)time.Sleep(1000 * time.Millisecond)}
}

从通道里读值,直到他被关闭为止,方法一:
可用于替代上面的方法一:

for {if s, ok := <-chan1; ok {fmt.Println(s)time.Sleep(1000 * time.Millisecond)} else {break}}

3,Goroutine池

生产者——消费者模型:
两个进程A和B共享一个固定大小的缓存区,A产生数据放入缓存区,B从缓存区中取出数据。
把A称做生产者,把B称作消费者。
缓存区满的时候,生产者休眠。
缓存区空的时候,消费者生产。

这一部分待完善。

4,互斥锁

目的:控制共享资源访问的方法

package mainimport ("fmt""sync"
)var (counter int//计数器wg      sync.WaitGroup//信号量mutex   sync.Mutex//互斥锁
)func main() {//1.两个信号量wg.Add(2)//2.开启两个线程go incCounter()go incCounter()//3.等待子线程结束wg.Wait()fmt.Println(counter)
}func incCounter() {defer wg.Done()//2.1.获取锁mutex.Lock()//2.2.计数加1counter++//2.3.释放独占锁mutex.Unlock()
}

Golang.Go语言基础相关推荐

  1. 【GoLang】GO语言系列--002.GO语言基础

    002.GO语言基础 1 参考资料 1.1 http://www.cnblogs.com/vimsk/archive/2012/11/03/2736179.html 1.2 https://githu ...

  2. golang快速入门--语言基础

    语言基础语法 行分隔符 在golang中,多个语句写在同一行,必须使用分号 " ; " 分隔开 注释 单行注释 使用// 即可表示 多行注释 使用/-/ 表示 字符串连接 允许使用 ...

  3. Go语言基础篇 (一)初识Golang

    文章目录 发展历程 语言特点 适用人群 我为什么学习Golang 注意事项 系列文章分类 下期预告 参考链接 发展历程 Go语言是在2007年由Robert Griesemer.Ken Thompso ...

  4. Golang 笔记 1 基础、基本数据类型

    一.Go语言基础 1. 基础 Go语言中的标识符必须以字母(Unicode字母,PHP/JS可以用中文作为变量名)下划线开头.大写字母跟小写字母是不同的:Hello和hello是两个不同的名字.    ...

  5. [GO语言基础] 三.变量声明、数据类型、标识符及编程练习12题

    作为网络安全初学者,会遇到采用Go语言开发的恶意样本.因此从今天开始从零讲解Golang编程语言,一方面是督促自己不断前行且学习新知识:另一方面是分享与读者,希望大家一起进步.前文介绍了Go的编译运行 ...

  6. [GO语言基础] 二.编译运行、语法规范、注释转义及API标准库知识普及

    作为网络安全初学者,会遇到采用Go语言开发的恶意样本.因此从今天开始从零讲解Golang编程语言,一方面是督促自己不断前行且学习新知识:另一方面是分享与读者,希望大家一起进步.前文介绍了什么是GO语言 ...

  7. 跟着邓神 3 天掌握 Go 语言基础(免费)

    有句话叫"看不起.看不清.追不上".近几年,关于 Go 与 Java 还有 C 的对比和讨论愈演愈烈,但不可否认的是,在十年多的时间里,Go 语言发展势头强劲,凭借其简洁.高效的特 ...

  8. golang go语言_在Go语言中无需反思即可使用Lodash的好处

    golang go语言 by Tal Kol 通过塔尔科尔 在Go语言中无需反思即可使用Lodash的好处 (The benefits of using Lodash in the Go langua ...

  9. go设置后端启动_Go语言基础(十四)

    Go语言基础(十四) 一.Redis 二.NSQ 三.Go module 四.Context......0 一.Redis Redis是一个key-value存储系统.和Memcached类似,它支持 ...

最新文章

  1. 国内首个面向工业级实战的点云处理课程
  2. ​20210716未来智能实验室收录资料
  3. Html.ActionLink
  4. Educational Codeforces Round 14 - F (codeforces 691F)
  5. 3个月亏17亿,贝壳释放了什么信号?
  6. service docker start后docker stop/waiting的解决方法
  7. 在Linux环境下select函数的初体验
  8. php中文分词nlp,几种常见的PHP中文分词系统
  9. 来自司徒正美的面试题
  10. VS2017报错 class “Cxxxx“没有成员“GetContextMenuManager“ “GetContextMenuManager“:不是“Cxxxx“的成员
  11. Problem : 救公主续
  12. 如何在 oracle 集群环境下修改私网信息 (文档 ID 2103317.1)
  13. 用java写银行存款对账,银行存款对账要怎么做
  14. AD7606分析讲解
  15. AI 仿人类人工智能(超级智能)的本质
  16. 学习线性代数-行列式 笔记(一)
  17. 香帅的北大金融学课笔记15 -- 大师投资智慧
  18. Linux搭建SVN Server
  19. 03-04年度国家级精品课程一览
  20. spyder pyecharts不显示_显示器需要定时校色吗?

热门文章

  1. 基于Android的医院预约挂号系统,基于Android的医院预约挂号系统设计与实现
  2. JAVA面试题大全(part_1)
  3. 爬取知乎,通过数据传输接口
  4. python基础练习-人名最多数统计
  5. 爬虫小程序 - 单词量测试
  6. 拓嘉启远电商:如何应付拼多多风控
  7. 2020阿里云服务器免费领取与互联教程
  8. MATLAB与DSP(C6657)的TCP/IP通信实现
  9. windows运行库文件
  10. sublime text 64位 3.3114 绿色汉化版下载地址