Go 语言被设计成一门应用于搭载 Web 服务器,存储集群或类似用途的巨型中央服务器的系统编程语言。对于高性能分布式系统领域而言,Go 语言无疑比大多数其它语言有着更高的开发效率。它提供了海量并行的支持,这对于游戏服务端的开发而言是再好不过了。

1.开发环境搭建

https://golang.google.cn/

2.基础

2.1编译

go run 文件名 —–> 直接运行文件

go run build 文件名.exe —->编译成exe可执行文件

go build -o 指定文件名.exe —–>指定编译的文件名

2.2转义字符

\t 制表符

\n 换行

\\ 斜杠\

\“ 分号”

\r 回车

2.3注释

//

/* */

2.4变量的声明

指定变量类型,但是不赋值,此时变量采用默认值

var i int
fmt.Println(i)

根据值自动推导类型

var name = "tom"
var num = 10

:= 左侧的变量不应该是已经声明过的

name := test

一次声明多个变量

var a,b = 10,12

一次声明多个变量并使用类型推导

a,b,c=10,"test",0

定义多个全局变量

var (
	v1 = "money"
	v2 = 10
)

3.数据类型

fmt.Printf("%T\n", c1) 打印出c1的类型

1.整形

int8 1字节

int16 2字节

int32 4字节

int64 8字节

int

uint

rune 与int32一样 等价int32,表示一个Unicode编码

byte 与int8等价

import "unsafe"

fmt.Print(unsafe.Sizeof(j))   //---> 8

2.浮点数

单精度float32

双精度float64

3.14e2 科学计数法 3.14*10^2

4.字符类型char

go没有装门的字符类型,可以用byte保存(ASCII码值)

go的字符串是由字节组成的

Go语言采用的编码是utf-8,解决了编码乱码的问题

var c2 int = '值'
fmt.Println(c2)
// ----->20540

5.布尔类型

只有false,true

6.字符串类型

字符串 一旦赋值就无法更改了,go中的字符串是不可变的

反引号输出

以字符串的原生形式输出,包括换行和特殊字符

fmt.Print(`\n test`)

字符串的拼接

fmt.Print("test" + "hahaha")

7.数据类型的默认值

var a int //0
var b float32 //0
var v float64 //0
var test bool //false
var name string //""

8.类型转换

1.类型转换

Golang中数据类型不能自动转换

T(v)
//T为数据类型 int32 float64
//v为要转换的变量和数据

Go的数据类型可以是范围小到范围大,也可以范围大到范围小

2.基本类型转string

通用:

%v	值的默认格式表示
%+v	类似%v,但输出结构体时会添加字段名
%#v	值的Go语法表示
%T	值的类型的Go语法表示
%%	百分号

布尔值:

%t	单词true或false

整数:

%b	表示为二进制
%c	该值对应的unicode码值
%d	表示为十进制
%o	表示为八进制
%q	该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示
%x	表示为十六进制,使用a-f
%X	表示为十六进制,使用A-F
%U	表示为Unicode格式:U+1234,等价于"U+%04X"

浮点数与复数的两个组分:

%b	无小数部分、二进制指数的科学计数法,如-123456p-78;参见strconv.FormatFloat
%e	科学计数法,如-1234.456e+78
%E	科学计数法,如-1234.456E+78
%f	有小数部分但无指数部分,如123.456
%F	等价于%f
%g	根据实际情况采用%e或%f格式(以获得更简洁、准确的输出)
%G	根据实际情况采用%E或%F格式(以获得更简洁、准确的输出)

字符串和[]byte:

%s	直接输出字符串或者[]byte
%q	该值对应的双引号括起来的go语法字符串字面值,必要时会采用安全的转义表示
%x	每个字节用两字符十六进制数表示(使用a-f)
%X	每个字节用两字符十六进制数表示(使用A-F)

指针:

%p	表示为十六进制,并加上前导的0x

fmt.Sprintf()

var a1 int = 10
var str string= fmt.Sprintf("%d", a1)
print(str)

strconv.FormatInt()

第一个参数是你要转换的整数。

第二个参数是基数,表示你希望整数以哪种进制格式来表示。例如,基数10表示十进制,基数16表示十六进制。

var str1 = strconv.FormatInt(10,10)

//'f'为格式 1表示保留一位小数   64表示为float64
var str2 = strconv.FormatFloat(89.99, 'f', 1, 64)

3.String转基本类型

ParseBool

func ParseBool(str string) (value bool, err error)

返回字符串表示的bool值。它接受1、0、t、f、T、F、true、false、True、False、TRUE、FALSE;否则返回错误。

ParseInt

func ParseInt(s string, base int, bitSize int) (i int64, err error)

返回字符串表示的整数值,接受正负号。

base指定进制(2到36),如果base为0,则会从字符串前置判断,”0x”是16进制,”0”是8进制,否则是10进制;

bitSize指定结果必须能无溢出赋值的整数类型,0、8、16、32、64 分别代表 int、int8、int16、int32、int64;返回的err是*NumErr类型的,如果语法有误,err.Error = ErrSyntax;如果结果超出类型范围err.Error = ErrRange。

ParseUint

func ParseUint(s string, base int, bitSize int) (n uint64, err error)

ParseUint类似ParseInt但不接受正负号,用于无符号整型。

ParseFloat

func ParseFloat(s string, bitSize int) (f float64, err error)

解析一个表示浮点数的字符串并返回其值。

如果s合乎语法规则,函数会返回最为接近s表示值的一个浮点数(使用IEEE754规范舍入)。bitSize指定了期望的接收类型,32是float32(返回值可以不改变精确值的赋值给float32),64是float64;返回值err是*NumErr类型的,语法有误的,err.Error=ErrSyntax;结果超出表示范围的,返回值f为±Inf,err.Error= ErrRange。

var str3 string = "true"
var bl bool
bl, _ = strconv.ParseBool(str3)
print(bl)

函数会返回两个值,一个是转换后的值,另一个是错误,可以使用_忽略error

如果string为”hello”并且转为int这时会采用int的默认值0

9.指针

&取地址

fmt.Printf("i的地址为", &a)

*解引用

var a int = 10
var ptr *int = &a
fmt.Printf("i的地址为\n", &a)
fmt.Printf("%d\n", *ptr)

10.运算符

与c语言类似

11.获取用户终端输入

fmt.Scanf()

fmt.Scanfln()

4.流程控制语句

if else else if

if 10 > 20 {
    fmt.Println("data1")
}else {
    fmt.Println("data2")
}

switch

switch的后面可以不用加break

switch(a){
    case "a":
    fmt.Print("test1")
    case "b":
    fmt.Print("test2")
    case "c":
    fmt.Print("test3")
    default:
    fmt.Print("test4")
}

for 循环

for i := 0; i < 100; i++ {
    fmt.Println("test")
}

练习 空心金字塔

for i := 0; i <= 9; i++ {
    for j := 1; j <= 9-i; j++ {
        fmt.Print(" ")
    }
    if i != 9 && i != 0 {
        fmt.Print("*")
        for j := 1; j <= i*2+1-2; j++ {
            fmt.Print(" ")
        }
        fmt.Print("*")
    } else {
        for j := 1; j <= i*2+1; j++ {
            fmt.Print("*")
        }
    }
    fmt.Println()
}

99乘法表

for i := 1; i <= 9; i++ {
    for j := 1; j <= i; j++ {
        fmt.Printf("%d * %d = %d\t", i, j, i*j)
    }
    fmt.Println()
}

goto 与C语言类似

5.函数

1.函数基本用法

func test5(a string, b string) float64 {
	var res1, res2 float64
	res1, _ = strconv.ParseFloat(a, 64)
	res2, _ = strconv.ParseFloat(b, 64)
	return res1 + res2
}

打包的基本语法

package util

引入包的基本语法

import “包的路径” 一般路径是GOPATH的src目录下开始

首字母大写类似public,小写为private

go语言不支持重载

//如果要导出包,则将首字母大写
func ha() {
	fmt.Print("ha")
}

2.go语言支持使用变量接收函数

func fun(a int ,b int) int {
	return a+b
}

func main(){
	a := fun
	var res int = a(10,20)
	fmt.Print(res)
}

并且可以当做参数传递

func fun(a int, b int) int {
	return a + b
}

func fun1(funcvar func(int, int) int, c int) int {
	return funcvar(10, 40) + c
}

func main() {
	a := fun
	var res int = a(10, 20)
	fmt.Println(res)
	var res1 = fun1(a, 30)
	fmt.Println(res1)
}

go支持自定义数据类型

type myInt int
var v myInt = myInt(90)
print(v)

go支持给返回值命名

func rs(a int,b int)(sum int,sub int){
	sum = a + b
	sub = a - b
	return 
}

可以使用args…类型接收多个参数

args的类型为Slice可以用[index]访问

func add(args ...int) int {
	var res int
	for i := 0; i < len(args); i++ {
		res += args[i]
	}
	return res
}

3.init函数

在main函数之前执行,用于基本构建

被引入的包的init会先执行

func init() {
	fmt.Print("init\n")
}

4.匿名函数

在定义的同时就完成了调用

func (a int,b int) int{
    return a+b
}(10,30)

将匿名函数赋值给变量

fu := func (a float64,b float64) float64 {
    return a * b
}

将匿名函数赋值给全局变量此时该匿名函数为全局匿名函数

5.defer

即延迟(defer)语句,你可以在函数中添加多个defer语句。当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。特别是当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然很容易造成资源泄露等问题

将defer语句放入到栈时,其相关的值也会拷贝一份

遵守先进后出的规则

func ReadWrite() bool {
    file.Open("file") //打开file文件
    defer file.Close()
    if failureX {
        return false
    }
    if failureY {
        return false
    }
    return true
}

defer在函数执行完毕后,可以及时释放函数创建的资源。

当函数完毕后,系统会依次从defer栈中。取出语句,关闭资源

6.字符串常用的函数

1.len

len(str) ===>统计长度

按字节返回

fmt.Print(len("test"))   //==>4
//遍历中文
str := "你好"
var res = []rune(str)
for i := 0; i < len(res); i++ {
    fmt.Printf("%c\n", res[i])
}

2.Atoi

将字符串转为整数

n, err := strconv.Atoi("123")
if err != nil {
    fmt.Println("转换错误", err)
} else {
    fmt.Println("转换成功", n)
}

3.ItoA

将整数转为字符串

4.字符串转整数

ns := strconv.Itoa(123)
fmt.Printf("%s\n", ns)

5.字符串转为byte

var bytes = []byte("gogogo")
fmt.Printf("bytes=%v\n", bytes)

6.byte转为字符串

var strs = string([]byte{97, 98, 99})
fmt.Printf("strs=%v\n", strs)

7.10转2,8,16进制

返回值为字符串

str = strconv.FormatInt(123,2); //--->转为2进制

8.查找子串是否在字符串内

判断str是否有substr

fmt.Println(strings.Contains("hahaha", "ha"))  //==>true
fmt.Println(strings.Count("hahaha","ha"))  //文本内有几个substr  ===>3
fmt.Println(strings.EqualFold("abc","ABC"))  //比较字符串,不区分大小写  ==>true
fmt.Println(strings.Index("abc","b")) //查找substr第一次在str出现的位置,没有为-1   ===>1
strings.LastIndex()  //===>与index相同,取得是最后一次出现的位置

9.替换字符

子字符串 需要替换的字符串 替换成的字符串 替换的执行次数

strings.Replace("go go go hello","go","golang",1)

10.分割字符

strings.Split("10,20,30,40",",") //===>拆分字符串","为拆分的字符

11.大小写转换

Strings.ToLower("GO")
strings.ToUpper("go")

12.去掉空格

strings.TrimSpace(" space ") //====>去掉左右两边的字符串
strings.Trim("hahah!","!") //=====>去掉左右两边的!字符
strings.TrimLeft()  //=====>去掉左两边的!字符
strings.TrimRight()  //=====>去掉右两边的!字符

13.判断字符

strings.hasPrefix("haha","ha")  //判断是否以ha开头
strings.HasStuffix("haha","ha") //判断是否以ha结尾

7.日期函数的使用

1.基本使用

now := time.Now()
fmt.Printf("%T,%v", now, now)

//===>time.Time,2024-08-29 17:12:07.0249172 +0800 CST m=+0.002417901

获取年月日时分秒

fmt.Print(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())

time.format(“2006/01/02 15:04:05”)

字符数字不能改动

time.format(“2006-01-02 15:04:05”)

2.时间的常量

Sleep(d Duration) ==>休眠的时间

Hour 小时

Minute 分钟

Second 秒

Millisecond 毫秒

Microscend 微秒

Nanosecond Duration 纳秒

now.Unix()   //时间戳
now.UnixNano()  //时间戳微秒

3.go的错误执行机制

当go程序出现异常时,会抛出panic的异常,然后在defer中用recover捕获这个异常,然后正常处理

func test() {
	defer func() {
		err := recover() //可以捕获到异常
		if err != nil {
			fmt.Println("err=", err)
		}
	}()
	num1 := 10
	num2 := 0
	res := num1 / num2
	fmt.Println(res)
}

func main() {
	test() //==>err= runtime error: integer divide by zer
}

4自定义错误

errors.New(“错误说明”),返回一个error类型的值,表示一个错误

func readfile(name string) (error error) {
	if name == "123" {
		return nil
	} else {
		return errors.New("读取文件错误")
	}

}

func testfile() {
	err := readfile("12")
	if err != nil {
		panic(err)
	}
	fmt.Println("成功执行")
}

func main() {
	testfile()   //==>读取文件错误
}

8.数组

1.定义

var test[10] float64
for i := 0; i < len(test); i++ {
    fmt.Println(i)
}
var num = [...]int{8, 9, 10}
fmt.Println("num=", num)

//1为索引  索引:值
var num1 = [...]int{1: 800, 0: 1000, 2: 888}
fmt.Println("num1=", num1)

2.遍历

index索引

value值

var num1 = [...]int{1: 800, 0: 1000, 2: 888}
fmt.Println("num1=", num1)

for index, value := range num1 {
fmt.Printf("index=%d,value=%d\n", index, value)
}

3.切片

切片是数组的引用,切片的长度可变

var 变量名 []类型
var intarr [5]int = [...]int{1, 2, 3, 4, 5}

//引用intarr的下标1-3的位置(不包含3)
slice := intarr[1:3]
fmt.Println("slice的元素", slice)
fmt.Println("slice的长度", len(slice))
fmt.Println("slice的容量", cap(slice))

slice的元素 [2 3]
slice的长度 2
slice的容量 4

slice底层是一个结构体

  • ptr*[2]int 指针引用的位置
  • len int 切片长度
  • cap int 容量

定义切片

var 切片[]type = make([],len,[cap])
var test []float64 = make([]float64, 5, 10)
fmt.Println(test)

4.切片注意事项

arr[0:len(arr)] 简写为arr[:]

切片定义完后不能直接使用,需要引用一个数组,或者make一个空间来使用

切片可以再次切片,同时切边与使用它切片而成的切片共用同一个内存地址,因此一个改变,两个都会变

var slice1 []int = []int{100, 200, 300}
//通过append给slice追加元素
slice1 = append(slice1, 2)
fmt.Println(slice1)
//将自身追加给自身
slice1 = append(slice1, slice1...)
fmt.Println(slice1)

使用copy完成拷贝

var a []int = []int{10, 20, 30, 40, 50}
var slice2 = make([]int, 10, 20)
copy(slice2, a)
fmt.Println(slice2)

slice具有引用传递

func test(slice []int){
	slice[0] =100
}
//当传递一个切片时,该切片会改变

5.修改字符串

将string转为[]byte 或者[]rune ->修改 ->重写成string

var str string = "this is life"
arr := []byte(str)
arr[1] = 'a'
str = string(arr)
fmt.Println(str)

修改为中文

var str string = "this is life"
arr := []rune(str)
arr[1] = '好'
str = string(arr)
fmt.Println(str)

9.Map

1.map

给一个空map赋值报错,在使用前需要先make,给map分配数据空间

map的key-value是无序的

var a map[string]string
a["1"] = "一"
fmt.Println(a) //报错
var a map[string]string = make(map[string]string, 10)
a["1"] = "一"
fmt.Println(a)

定义一个map,map里面还是map

var students map[string]map[string]string = make(map[string]map[string]string, 3)
students["1001"] = map[string]string{"name": "tom", "age": "18"}
students["1002"] = map[string]string{"name": "jack", "age": "19"}
students["1003"] = map[string]string{"name": "jerry", "age": "20"}
fmt.Println(students)

2.更新修改

map[key] = “value” 如果有就是覆盖,没有就是增加

delete(map名,key)

查找键值对

cal, ok := a["1"]
if ok {
    fmt.Println(cal)
} else {
    fmt.Println("不存在")
}

3.map的遍历

var a map[string]string = make(map[string]string, 10)
a["1"] = "一"
a["2"] = "二"
a["3"] = "三"
fmt.Println(a)

for i, v := range a {
    fmt.Printf("i=%v\t v=%v\n", i, v)
}
//计算长度
length := len(a)
fmt.Println(length)

4.map切片

[]map[string]string

var cityies = []map[string]string{}
cityies = make([]map[string]string, 3)

cityies[0] = make(map[string]string, 2)
cityies[1] = make(map[string]string, 2)

cityies[0]["guangdong"] = "guangzhou"
cityies[1]["hunan"] = "changsha"
fmt.Println(cityies)

通过append动态添加属性

//动态增加
new_city := map[string]string{
    "beijing": "beijing",
}
cityies = append(cityies, new_city)
fmt.Println(cityies)

5.map的排序

map原本是无序的

map1 := make(map[int]int)
map1[10] = 100
map1[3] = 90
map1[7] = 5
map1[20] = 30

将map的key放入切片中

对切片进行排序

遍历切片,按照key来输出map的值

var keys []int
for k, _ := range map1 {
    keys = append(keys, k)
}
sort.Ints(keys)
fmt.Println(keys)
for _, k := range keys {
    fmt.Printf("map1[%v]=%v\n", k, map1[k])
}

10.面向对象

1.stuct

字段为大写开头 –>public 小写–>private

type 结构体名 struct{
	field1 type
	field2 type
}

2.声明结构体

var person Person
var person Person = Person{}

var person *Person = new(Person)
(*person).属性="值"
//也可以简化访问


var person *Person = &Person()

3.序列化

通过tag给struct指定tag,用于序列化,保证兼容性

type Person struct{
	name string  `json:"name"`
	age int  `json:age`
}
//序列化
jsonStr, _ := json.Marshal(map1)
fmt.Println(jsonStr)

2.方法

type Person struct {
	name string
	age  int
}

// 给person绑定一个方法
func (p Person) test() {
	fmt.Println("test")
}

3.继承

嵌套匿名结构体实现继承

type Animal struct{
	name string
	move string
}

type Dog struct{
	Animal   //嵌套匿名结构体实现继承
	age int
}

匿名结构体字段访问可以简化

直接变量名.继承的父类的属性

4.多重继承

可以嵌入一个基本数据类型

type A struct{
	Name string
}

type B struct{
	int
	A
}
//访问 
var b B
b.int = 30

嵌套多个匿名结构体,实现多重继承

5.接口

interface不能包含任何变量

//定义接口
type C interface {
	Start()
	End()
}

type D struct {
}
//实现接口方法
func (d D) Start(){

}

func (d D) End(){
	
}

一个自定义类只有实现了某个接口,才能将自定义类型的实例赋值给接口类型

只要是自定义类型都可以实现接口,不仅仅是结构体

func (i integer)Start(){
	
}

6.类型断言

b = a.(类型) 类型断言

func main(){
	var a interface{}
	var b float32 = 10
	a = b
	b = a.(float32)
}

在进行断言时,带上检测机制

if b, ok := a.(float64); ok {
    fmt.Printf("%T %v", b, b)
} else {
    fmt.Printf("error")
}
func TypeJudge(items ...interface{}) {
	for index, x := range items {
		switch x.(type) {
		case bool:
			fmt.Printf("%d %v", index, x)
		case int:
			fmt.Printf("%d %v", index, x)
		default:
			fmt.Printf("%d %v", index, x)
		}
	}
}

11.文件操作

1.os.Open

file.Close()

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

func main() {
	file, err := os.Open("D:\\MYcode\\code_go\\src\\ts.txt")

	if err != nil {
		fmt.Println(err)
	}

	defer file.Close()

	reader := bufio.NewReader(file)
	for {
		//读到\n就结束
		str, err := reader.ReadString('\n')
		if err == io.EOF {
			break
		} else {
			fmt.Print(str)
		}
	}
}

1.写入文件

bufio.NewWriter

func main() {
	filePath := "D:/MYcode/code_go/src/abc.txt"
	file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		fmt.Println(err)
	}
	defer file.Close()
	writer := bufio.NewWriter(file)
	for i := 0; i < 5; i++ {
		writer.WriteString("Hello\n")
	}
	//将缓存的数据写入磁盘
	writer.Flush()
}

2.判断目录或文件是否存在

os.Stat()

err为nil存在 不为nil 不存在

3.将文件移动到另一个目录

filePath := "D:/MYcode/code_go/src/abc.txt"
NewfilePath := "D:/MYcode/code_go/src/abcd.txt"
content, err := ioutil.ReadFile(filePath)
if err != nil {
    fmt.Println(err)
}
err = ioutil.WriteFile(NewfilePath, content, 0666)

拷贝文件

io.Copy(writer,reader) 使用bufio.NewWriter bufio.NewReader获取writer,reader

4.接收参数的传递

func main() {
	fmt.Println(len(os.Args))
	for i, v := range os.Args {
		fmt.Println("args[%v]=%v", i, v)
	}
}

.\main.exe 1 go 2 go

5.获取解析命令行参数

var user string
var pwd string
var host string
var port int
flag.StringVar(&user, "u", "", "默认为空")
flag.StringVar(&pwd, "P", "", "默认为空")
flag.StringVar(&host, "h", "", "默认为空")
flag.IntVar(&port, "p", 3306, "默认为3306")

flag.Parse()
fmt.Printf("%v %v %v %v", user, pwd, host, port)

.\main.exe -u root -P 123456 -h 127.0.0.1 -p 3306

===>root 123456 127.0.0.1 3306

5.序列化json对象

1.序列化对象

student := Student{
    Name: "tom",
    Age:  18,
}

data, err := json.Marshal(&student)
if err != nil {
    fmt.Println(err)
}
fmt.Println(string(data))

2.序列化map

a := make(map[string]interface{})
a["name"] = "jerry"
a["age"] = 33

data, err = json.Marshal(a)
if err != nil {
    fmt.Println(err)
}
fmt.Println(string(data))

3.序列化切片

var slice []map[string]interface{}

var m1 map[string]interface{}
m1 = make(map[string]interface{})
m1["name"] = "alice"
m1["age"] = "14"

var m2 map[string]interface{}
m2 = make(map[string]interface{})
m2["name"] = "jack"
m2["age"] = "15"

slice = append(slice, m1)
slice = append(slice, m2)

data, err = json.Marshal(slice)
if err != nil {
    fmt.Println(err)
}
fmt.Println(string(data))

===>[{“age”:”14”,”name”:”alice”},{“age”:”15”,”name”:”jack”}]

6.序列化json时的tag的使用

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

7.反序列化

json.Unmarshal()

var mystudent Student

err = json.Unmarshal([]byte(string(data)), &mystudent)

if err != nil {
    fmt.Println(err)
}
fmt.Printf("{name:%v,age:%v\n}", mystudent.Name, mystudent.Age)

12.单元测试

testing框架

import "testing"

func TestAdd(t *testing.T) {
	res := Add(5)
	if res != 55 {
		t.Fatalf("执行错误,期望值%v,实际值%v", 55, res)
		return
	}
	t.Logf("成功执行")
}

func Add(a int) int {
	return a + 50
}

文件名以_test.go结尾

测试函数以Test开头

go test -v

Pass表示成功 Fail表示失败

13.go进程

1.go协程

go test() 函数调用开启协程

2.MPG模式

M操作系统的主线程

P协程需要的上下文

G协程

3.设置CPU核数

import (
	"fmt"
	"runtime"
)

func main() {
	//获取当前设备的CPU数
	num := runtime.NumCPU()
	fmt.Println(num)
	//设置最大使用的CPU数
	runtime.GOMAXPROCS(4)
}

4.全局互斥锁

lock sync.Mutex

//加锁

lock.Lock()

//解锁

lock.Unlock()

5.channel

channel是一个队列

channel是引用类型,需要make才能使用,管道是有类型的,intChar只能写入整数int

var intChan chan<- int 只写

var intChan <-chan int 只读

var intChan chan int
intChan = make(chan int)
fmt.Printf("%v", intChan)

写入

intChan <- 10

取出

num2 := <-intChan
fmt.Println(num2)

关闭

close(intChan)

遍历应该使用for range遍历,如果在遍历前没有关闭则会报错

6.管道和协程的共同使用

func putNumber(intChan chan int) {
	for i := 1; i <= 8000; i++ {
		intChan <- i
	}
	close(intChan)
}

func checkNumber(intChan chan int, resChan chan int, exitChan chan bool) {
	var flag bool
	for {
		num, ok := <-intChan
		if !ok {
			break
		}
		flag = true
		for i := 2; i < num; i++ {
			if num%i == 0 {
				flag = false
				break
			}
		}
		if flag {
			resChan <- num
		}
	}
	exitChan <- true
}

func main() {

	intChan := make(chan int, 1000)
	resChan := make(chan int, 2000)
	exitChan := make(chan bool, 4)
	go putNumber(intChan)
	go checkNumber(intChan, resChan, exitChan)
	go checkNumber(intChan, resChan, exitChan)
	go checkNumber(intChan, resChan, exitChan)
	go checkNumber(intChan, resChan, exitChan)

	go func() {
		for i := 0; i < 4; i++ {
			<-exitChan
		}
		close(resChan)
	}()
	for v := range resChan {
		fmt.Printf("取到的值%d\n", v)
	}
}

通过select解决管道阻塞问题

当阻塞时会自动过渡到下一个case

select {
	case v: <- intChan:
		fmt.Println("读取")
}

使用recover解决管道阻塞问题

defer func() {
    err := recover() //可以捕获到异常
    if err != nil {
        fmt.Println("err=", err)
    }
}()

14.反射

反射(Reflection)在编程中通常被定义为在运行时检查程序的能力。这种能力使得一个程序能够操纵像变量、数据结构、方法和类型这样的对象的各种属性和行为。这一机制在Go中主要通过reflect标准库实现。

在Go中,接口(interface)和反射是紧密相关的。事实上,你可以认为接口是实现反射的“入口”。当你将一个具体类型的变量赋给一个接口变量时,这个接口变量内部存储了这个具体变量的类型信息和数据。

type Any interface{}

func inspect(a Any) {
    t := reflect.TypeOf(a)
    v := reflect.ValueOf(a)
    fmt.Println("Type:", t, "Value:", v)
}

func main() {
    var x int = 10
    var y float64 = 20.0
    inspect(x) // Type: int Value: 10
    inspect(y) // Type: float64 Value: 20
}

1.使用注意事项

reflect.Value.Kind() 获取变量的类别,返回的是一个常量

.Elem()相当于取到了地址,并且使用setInt()方法

func reflect1(b interface{}) {
	rVal := reflect.ValueOf(b)
	//tTyp := reflect.TypeOf(b)
	rVal.Elem().SetInt(10)
}

func main() {
	var num int = 10
	reflect1(&num)
	fmt.Println(num)
}

2.go反射的基本实践

import (
	"fmt"
	"reflect"
)

type Cal struct {
	Num1 int
	Num2 int
}

func (c Cal) GetSub(name string) {
	fmt.Printf("%s完成了%d-%d=%d", name, c.Num2, c.Num1, c.Num2-c.Num1)
}

func main() {
	var c *Cal
	var rval reflect.Value
	c = &Cal{}
	rval = reflect.ValueOf(c)
	rval = rval.Elem()
	rval.FieldByName("Num1").SetInt(3)
	rval.FieldByName("Num2").SetInt(8)

	//Call传递的参数为reflect.Value切片类型
	var params []reflect.Value
	params = append(params, reflect.ValueOf("tom"))
	rval.Method(0).Call(params)
}

15.网络编程

1.服务监听

func main() {
	listen, err := net.Listen("tcp", "0.0.0.0:8888")
	if err != nil {
		fmt.Println(err)
	}

	defer listen.Close() //延时关闭


	fmt.Printf("begin to listen%v", listen)
}

循环等待

func process(conn net.Conn) {
	defer conn.Close()

	for {
		buf := make([]byte, 1024)

		n, err := conn.Read(buf)
		//n为接收的字节数
		if err != nil {
			fmt.Println(err)
			return
		}

		fmt.Printf("recive message:%v", string(buf[:n]))
	}
}

func main() {
	listen, err := net.Listen("tcp", "0.0.0.0:8888")
	if err != nil {
		fmt.Println(err)
	}

	defer listen.Close() //延时关闭

	//循环等待
	fmt.Printf("begin to listen%v address\n", listen)
	for {
		conn, err := listen.Accept()
		if err != nil {
			fmt.Println(err)
		} else {
			fmt.Printf("connect %v", conn)

		}
		go process(conn)
	}
}

监听客户端,发送消息

func main() {
	//建立连接
	conn, err := net.Dial("tcp", "10.34.111.137:8888")
	if err != nil {
		fmt.Println(err)
	}
	//从控制台输入内容
	reader := bufio.NewReader(os.Stdin)
	strs, err := reader.ReadString('\n')
	if err != nil {
		fmt.Println(err)
	}
	//发送请求
	n, err := conn.Write([]byte(strs))
	if err != nil {
		fmt.Println(err)
	}
	fmt.Print(n)
}

循环发送客户端消息

func main() {
	//建立连接
	conn, err := net.Dial("tcp", "10.34.111.137:8888")
	if err != nil {
		fmt.Println(err)
	}

	//发送请求
	for {
		//从控制台输入内容
		reader := bufio.NewReader(os.Stdin)
		strs, err := reader.ReadString('\n')
		if err != nil {
			fmt.Println(err)
		}
		strs = strings.Trim(strs, " \r\n")
		if strs == "exit" {
			return
		} else {
			n, err := conn.Write([]byte(strs))
			if err != nil {
				fmt.Println(err)
			}
			fmt.Print(n)
		}

	}
}

16.Redis+Go

1.redigo

package main

import (
	"fmt"
	"redigo-master/redis"
)

func main() {
	conn, err := redis.Dial("tcp", "127.0.0.1:6379")
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println(conn)
}

2.go操作redis(string类型)

func main() {
	conn, err := redis.Dial("tcp", "127.0.0.1:6379")
	if err != nil {
		fmt.Println(err)
	}
	defer conn.Close()
	fmt.Println(conn)

	_, err = conn.Do("set", "name", 6)
	if err != nil {
		fmt.Println(err)
	}
    value, err := redis.Int(conn.Do("get", "name"))
	if err != nil {
		fmt.Println(err)
	}
	fmt.Printf("%c", value)
}

3.go操作redis(hash类型)

func main() {
	conn, err := redis.Dial("tcp", "127.0.0.1:6379")
	if err != nil {
		fmt.Println(err)
	}
	defer conn.Close()

	_, err = conn.Do("HSet", "value", "name", "name")
	if err != nil {
		fmt.Println(err)
	}
	value, err := redis.String(conn.Do("HGetall", "value", "name"))
	if err != nil {
		fmt.Println(err)
	}
	fmt.Printf("%s", value)
}

获取所有的字段和值

value, err := redis.Strings(conn.Do("HGetall", "value"))

设置过期时间

conn.Do("expire", "value", "name", 10) //10秒过期

4.Redis连接池