1 包
包可以区分命名空间,一个文件夹中不能有两个同名文件,go中创建一个包一般是创建一个文件夹,在该文件夹里面的go文件中使用关键字package声明包名称,通常文件夹名称和包名称相同,并且一个文件夹下面只有一个包
创建包
创建一个名为dao的文件夹
创建一个dao.go文件
在该文件中声明包
package dao
import "fmt"
func Test1(){
fmt.PrintLn("test package")
}
导入包
要使用某个包下面的方法或者变量 需要导入该包,导入需要从GOPATH开始包路径,例如在service.go中导入dao包
package main
import "dao"
func main(){
dao.Test1()
}
- 。一个文件夹下只能有一个package
- 。 import 后面的其实是 GOPATH 开始的相对目录路径,包括最后一段。但由于一个目录下只能有一个package,所以import 一个路径就等于是 import 了这个路径下的包。
- 。注意,这里指的是“直接包含”的8o文件。如果有子目录,那么子目录的父目录是完全两个包。·比如你实现了一个计算器package,名叫 calc,位于 calc 目录下; 但又想给别人一个使用范例,于是在calc下可以建个example子目录(calc/example/),这个子目录里有个example.go (calc/example/example.go)。此时,example.go可以是main包,里面还可以有个main函数。
- 一个package的文件不能在多个文件夹下
- 。如果多个文件夹下有重名的package,它们其实是彼此无关的package
- 。如果一个go文件需要同时使用不同目录下的同名package,需要在 import 这些目录时为每个目录指定-个package的别名
2 GO Module 包管理工具
go module 是golang 1.11新加的特性,用来管理模块中包的依赖关系
go mod 使用方法
初始化模块
go mod init <项目模块名称>
依赖关系处理,根据go.mod 文件
go mod tidy
将依赖包复制到项目下的vendor 目录
go mod vendor //如果包被屏蔽,可以使用这个命令 ,随后使用 go build -mod =vendor 编译
显示依赖关系
go list -m all
显示详细的依赖关系
go list -m -json all
下载依赖
go mod download [path@version]
3 并发编程-协程
golang中并发是函数相互独立运行的能力 goroutines是并发允许的函数,golang提供了Goroutines 作为并发处理操作的一种方式
创建协程非常简单,就是在一个任务函数前面加一个关键字 go
go task()
package main
import(
"fmt"
"time"
)
func showMsg(msg string) {
for i := @;i< 5;i++ (
fmt.Printf("msg: %v\n", msg)
time.sleep(time.Millisecond * 100)
}
}
func main(){
go showMsg("java")
go showMsg("golang")
time.Sleep(time.second*3)
fmt.Pringln("end...")
主函数退出,程序自动就结束了,同时杀死协程函数
}
4 协程之通道channel(解决协程之间的通信)
Go 提供了一种称为通道的机制,用于在 goroutine 之间共享数据。当您作为 goroutine 执行并发活动时,需要在goroutine 之间共享资源或数据,通道充当 goroutine 之间的管道(管道)并提供一种机制来保证同步交换
需要在声明通道时指定数掘类型。我们可以共享内置、命名、结构和引用类型的值和指针。数据在通道上传递:在任何给定时间只有一个 goroutine 可以访问数据项: 因此按照设计不会发生数据竞争。
根据数据交换的行为,有两种类型的通道: 无缓冲通道和缓冲通道。无缓冲通道用于执行 goroutine 之间的同步通信,而缓冲通道用于执行异步通信。无缓冲通道保证在发送和接收发生的瞬间执行两个 goroutine 之间的交换。缓冲通道没有这样的保证。
通道由 make 函数创建,该函数指定 chan 关键字和通道的元素类型
这是创建无缓冲和缓冲通道的代码块:
语法
Unbuffered := make(chan int) // 整型无缓冲酒道
buffered := make(chan int,10) // 整型有缓冲通道
使用内置函数 make 创建无缓冲和缓冲通道,make 的第一个参数需要关键字 chan ,然后是通道允许交换的数据类型。
这是将值发送到通道的代码块需要使用<- 运算符:
语法
goroutine1 := make(chan string,5) // 字特事缓冲通道
goroutine1 <- “Australia” // 通过通道发送字符串
一个包含5个值的缓冲区的字符串类型的goroutinel通道,然后我们通过通道发送字符串 Australia
这是从通道接收值的代码块
语法
data := <-goroutinel //从通道中接收字符串
<- 运算法附加到通道变量goroutinel的左侧 以接收来自通道的值
无缓冲通道
在无缓冲通道中,在接收到任何值之前没有能力保存它。在这种类型的通道中,发送和接收 goroutine 在任何发送或接收操作完成之前的同一时刻都准备就绪。如果两个 goroutine 没有在同一时刻准备好,则通道会让执行其各自发送或接收操作的 goroutine 首先等待。同步是通道上发送和接收之间交互的基础。没有另一个就不可能发生。
缓冲通道
在缓冲通道中,有能力在接收到一个或多个值之前保存它们。在这种类型的通道中,不要强制 goroutine 在同一时刻准备好执行发送和接收。当发送或接收阻塞时也有不同的条件。只有当通道中没有要接收的值时,接收才会阻寒。仅当没有可用缓冲区来放置正在发送的值时,发送才会阻寒
通道的发送和接收特性
1,对于同一个通道,发送操作之间是互斥的,接收操作之间也是互斥的。
2.发送操作和接收操作中对元素值的处理都是不可分割的。
3.发送操作在完全完成之前会被阻塞。接收操作也是如此.
package main
import (
"fmt"
"math/rand"
"time"
)
var values = make(chan int)
func send() {
rand.Seed(time.Now().UnixNano())
value := rand.Intn(10)
fmt.Println("send %v\n", value)
values <- value
}
func main() {
defer close(values) //延迟关闭通道
go send()··
fmt.Println("wait ...")
value := <-values
fmt.Println("receive %v\n", value)
fmt.Println("end ...")
}
5 协程同步实现-WaitGroup
主要解决 协程没有运行完,但是主函数已经完成时,同步杀死协程的问题;
package main
import (
"fmt"
"sync"
)
var wp sync.WaitGroup
func main() {
for i := 0; i < 10; i++ {
go showMsg(i)
wp.Add(1) //每次等待值加1
}
wp.Wait() 查看等待值是否为0 不为0 等待执行完成后再结束
fmt.Println("end...")
}
func showMsg(i int) {
defer wp.Done() //延迟处理等待值减一
fmt.Printf("I: %v\n", i)
}
6 Runtime包中协程管理操作
runtime.Gosched()
出现主协程和子协程争抢运行时 让出cpu时间片,重新等待安排任务
package main
import (
"fmt"
"runtime"
)
func main() {
go show("go runtime")
for i := 0; i < 10; i++ {
runtime.Gosched()
fmt.Println("golang")
}
}
func show(s string) {
for i := 0; i < 2; i++ {
fmt.Println(s)
}
}
runtime.Goexit() 直接退出协程
package main
import (
"fmt"
"runtime"
)
func main() {
go show("go runtime")
for i := 0; i < 10; i++ {
runtime.Gosched()
fmt.Println("golang")
}
}
func show(s string) {
for i := 0; i < 10; i++ {
if i >=5{
runtime.Goexit()
}
fmt.Println(s)
}
}
runtime.GOMAXPROCS(2) 设置cpu核心数
7 并发实现同步-Mutex互斥锁
除了channel实现同步之外,还可以使用Mutex互斥锁的方式实现同步
package main
import (
"fmt"
"sync"
)
var i int = 100
var wg sync.WaitGroup
var lock sync.Mutex
func add() {
defer wg.Done()
lock.Lock() //加锁
i += 1
fmt.Println("i++:", i)
lock.Unlock() //解锁
}
func sub() {
defer wg.Done()
lock.Lock()
i -= 1
fmt.Println("i--", i)
lock.Unlock()
}
func main() {
for i := 0; i < 100; i++ {
wg.Add(1)
go add()
wg.Add(1)
go sub()
}
wg.Wait()
fmt.Println("end ...", i)
}
通道里面必须使用close方法关闭通道:如果不关闭的话,则在写少读多的时候 会出现死锁现象
通道chan的循环操作
package main
import "fmt"
var c = make(chan int)
func main() {
go func() {
for i := 0; i < 20; i++ {
c <- i
}
close(c) //如果不关闭 则读的次数超出通道数量,则出现死锁
}()
//循环方式1
r := <-c
fmt.Println(r)
r = <-c
fmt.Println(r)
//循环方式2
for i := 0; i < 2; i++ {
r := <-c
fmt.Println(r)
}
//循环方式3
for v := range c {
fmt.Println(v)
}
//循环方式4
for {
v, ok := <-c
if ok {
fmt.Println(v)
} else {
break
}
}
}
8 协程-定时器:Timer (执行一次)
timer 定时器,可以实现一下定时操作,内部通过channel来实现
package main
import (
"fmt"
"time"
)
func main() {
timer := time.NewTimer(time.Second * 2) //延迟两秒
fmt.Println("时间", time.Now())
t1 := <-timer.C //通道阻塞,直到时间达到了才执行
fmt.Println(t1)
stop := timer.Stop() //结束阻塞,直接运行下面的内容 当该运行到这个位置时计时器停止
fmt.Println(stop)
timer.Reset(time.Second * 2) //重新设置时间,修改newTimer时间
}
9 并发编程-Ticker 定期执行定时器(周期执行)
timer只执行一次,ticker周期性执行
package main
import (
"fmt"
"time"
)
func main() {
ticker := time.NewTicker(time.Second)
count := 1
for _ = range ticker.C {
fmt.Println("ticker...")
count++
if count > 5 {
ticker.Stop()
break
}
}
}
结果
ticker...
ticker...
ticker...
ticker...
ticker...
10 异步的原子操作sync/atomic
atomic 包中提供了如下以Add为前缀的增减操作:
- func AddInt32(addr *int32,delta int32) (new int32)
- func AddInt64(addr *int64.delta int64) (new int64)
- func AddUint32(addr *uint32, delta uint32) (new uint32)
- func AddUint64(addr *uint64, delta uint64) (mew uint64)
- func AddUintptr(addr *uintptr, delta uintptr) (new uintptr)
atomic.LoadInt32(&i) //读的时候查看值锁
atomic.StoreInt32(&i,200) //写的时候查看原子操作
atomic.CompareAndSwapInt32(&i,100,200) //将i的值从100改为200 如果原值不是100则不交换
package main
import (
"fmt"
"sync/atomic"
"time"
)
var i int32 = 100
func add() {
atomic.AddInt32(&i, 1)
}
func sub() {
atomic.AddInt32(&i, -1)
}
func main() {
for i := 0; i < 100; i++ {
go add()
go sub()
}
time.Sleep(time.Second * 2)
fmt.Println("end ...", i)
}
11 文件管理操作
package main
import (
"fmt"
"os"
)
//创建文件
func createFile() {
f, err := os.Create("a.txt")
if err != nil {
fmt.Println(err)
} else {
fmt.Println(f.Name())
}
}
//创建目录
func createDir() {
err :=os.Mkdir("test",os.ModePerm) //创建一个目录
if err != nil{
fmt.Println(err)
}
err =os.MkdirAll("test/test",os.ModePerm)//创建多级目录、
if err != nil {
fmt.Println(err)
}
}
//删除文件和目录
func remove(){
err :=os.Remove("a.txt")
if err != nil {
fmt.Println(err)
}
err =os.RemoveAll("test")
if err != nil {
fmt.Println(err)
}
}
func main() {
createFile()
createDir()
remove()
}
读文件
func readops() {
f, err := os.Open("a.txt")
if err != nil {
fmt.Println(err)
}
for {
buf := make([]byte, 10)//每次缓存区查询字节长度
n, err1 := f.Read(buf)
if err1 == io.EOF {
return
}
fmt.Println(n) //查询结果自己长度
fmt.Println(string(buf)) //查询内容
}
}
读目录 f.ReadDir()
func readDir() {
dir, _ := os.ReadDir("user")
for _, v := range dir {
fmt.Println(v.IsDir()) //判断是否目录
fmt.Println(v.Name()) //文件名或者目录名
}
}
文件写操作 os.O_RDWR (在文件前面写)os.O_APPEND(文件末尾追加)O_TRUNC (覆盖写)
func write() {
f, _ := os.OpenFile("a.txt", os.O_RDWR|os.O_APPEND, 0777)
f.Write([]byte("这是一个内容")) //写字节
f.WriteString("sadfajslkdfjqweo爱上的发就算了打飞机去了为") //写字符串
f.Close();
}
// 跌得当前正在运行的速程1d
fmt.Printf("os.Getpid(): sw'n". os.Getpid())
// 父id
fmt.Printf("os.Getppid(): tw'n". os.Getppid())
//设置新进程的属性
attr :m =&os.ProcAttri
//fles指定新进程继示的活动文件对象
//前三个分别为。标准输入、标准输出、标准增课输出
Files: = os.Filelos.Stdin. os.Stdout. os.Stderr}.
//新进程的环境交量
Env: os.Environ().
//开她一个新连程
p.err :m os.StartProcess("C:liWindowsiiSystem321inotepad.exe"[]lstring{"C:\imindowsliSystem32\\motepad.exe"."D:\la.txt"t. attr)if err iw ni] (
fat.Printin(err)
fmt.Printin(p)
fmt.Printiln("进程ID,",p.Pid)
//通过进程ID查找进程
p2. := os.FindProcess(p.Pid)
12 标准库中os包和环境变量的方法
s := os.Environ() //获取所有环境变量
fmt.Println(s)
s1 := os.Getenv("GOPATH") //获取指定环境变量
fmt.Println(s1)
s2 := os.Setenv("env1","env1") //设置或者更改环境变量
fmt.Println(s2)
s3,b := os.LookupEnv("env1")//查询环境变量是否存在
fmt.Println(s3)
fmt.Println(b)
os.Clearenv() //清空环境变量
13 标准库IO包Input output 读写
golang标准库io包input output
Go 语言中,为了方便开发者使用,将 10 操作封装在了如下几个包中:
- 。io 为10 原语 (O primitives) 提供基本的接口 os File Reader Writer
- 。io/ioutil 封装一些实用的/O 函数
- 。fmt 实现格式化1/0,类似 C语言中的 printf 和 scanf I
- 。bufio 实现带缓冲1/0
io-基本的10 接口
在io 包中最重要的是两个接口: Reader和 Writer 接口。本章所提到的各种10 包,都跟这两个接口有关,也就是说,只要实现了这两个接口,它就有了 10 的功能
Reader接口
type Reader interface (
Read(p [jbyte) (n int, err error)
Writer接口
type Writer interface (
Write(p []byte) (n int, err error)
那些类型实现了Reader和Writer接口
os.File 同时实现了 io.Reader 和 ioWriter
strings.Reader 实现了 io.Reader
bufio.Reader/Writer 分别实现了 io.Reader 和 io.Writer
bytes.Buffer 同时实现了 io.Reader 和 io.Writer
bytes.Reader 实现了 io.Reader
compress/gzip.Reader/Writer 分别实现了 io.Reader 和 io.Writer
crypto/cipher,StreamReader/StreamWriter 分别实现了 o.Reader 和 io.Writer
crypto/tls.Conn 同时实现了 io.Reader 和 io.Writer
encoding/csv.Reader/Writer 分别实现了 io.Reader 和 io.Writer
14 标准库ioutil包
封装一些实用的1/0 函教
ReadAll 读取数据,返回读到的字节 slice
ReadDir 读取一个目录,返回目录入口数组 Dos.Filelnfo
ReadFile 读一个文件,返回文件内容(字节slice)
WriteFile 根据文件路径,写入字节slice
TempDir 在一个目录中创建指定前缓名的临时目录,返回新临时目录的路径
TempFile 在一个目录中创建指定前缀名的临时文件,返回 os.File
使用ioutil读取文件时
f, _ := os.Open("a.txt")
defer f.Close()
b, err := ioutil.ReadAll(f)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(b))
15 标准库 log
go内置了log 包实现简单日志服务,通过调用log包的函数实现简单日志打印功能
log包邮三个系列日志打印函数
print 单纯打印日志
panic 打印日志 抛出panic异常 defer 会执行
fatal 打印日志,强制结束程序os.exit(1),defer函数不会执行
log.Print("日志简单输出")
log.Println("输出并换行")
log.Printf("格式化输出 %d", 100)
log.Panic("日志,抛出异常 后面代码不再执行")
log.Panicf("日志,抛出异常 后面代码不再执行 格式化输出")
log.Panicln("日志,抛出异常 后面代码不再执行 换行输出")
panic() 方法 抛出异常结束运行 结束前会执行defer的内容
16 new 和 make 区别:
1. make 只能用来分配及初始化类型为 slice ,map ,chan 的数据; new 可以分配任意类型的数据
2. new 分配返回的是指针,即类型 *T ; make 返回引用,即T:
3. new 分配的空间被清零, make 分配后,会进行初始化
make
内建函数make(T args)与new(T)的用途不一样。它只用来创建slice,map和channel,并且返回一个初始化的(而不是置零),类型为T的值(而不是"T)。之所以有所不同,是因为这三个类型的背后引用了使用前必须初始化的数握结构。例如,slice是一个三元描述符,包含一个指向数据(在数组中)的指针,长度,以及容量在这些项被初始化之前,slice都是nil的。对于slice,map和channel,make初始化这些内部数据结构,并准备好可用的值。
make([]lint,10,100)
分配一个有100个int的数组,然后创建一个长度为10,容量为100的slice结构,该slice引用包含前10个元素的数组。
对应的,new(lint返回一个指向新分配的,被置零的slice结构体的指针,即指向值为nil的slice的指针。
17 类型转换
var i int = 1
var j byte = 2
//类型转换
j = byte(i)
fmt.Printf("j: %v\n", j)
//查询是否包含
b := []byte("duoke368.com") //字符串强转为byte切片
sublice1 := []byte("duoke36a")
sublice2 := []byte("DuoKe368")
fmt.Println(bytes.Contains(b, sublice1)) //true
fmt.Println(bytes.Contains(b, sublice2)) //false
//查询出现的次数
s := []byte("hellooooooooo")
sep1 := []byte("h")
sep2 := []byte("1")
sep3 := []byte("o")
fmt.Println(bytes.Count(s, sep1)) //1
fmt.Println(bytes.Count(s, sep2)) //2
fmt.Println(bytes.Count(s, sep3)) //9
//重复出现
b = []byte("hi")
fmt.Println(string(bytes.Repeat(b, 1))) //hifmt.Println(string(bytes .Repeat(b,3))) //hihihi
//替换字符串
s = []byte("hello,world")
old := []byte("o")
news := []byte("ee")
fmt.Println(string(bytes.Replace(s, old, news, 0))) //hello, world
fmt.Println(string(bytes.Replace(s, old, news, 1))) //hellee, world
fmt.Println(string(bytes.Replace(s, old, news, 2))) //hellee, weerldnews ,
fmt.Println(string(bytes.Replace(s, old, news, -1))) //hellee, weerld
//字符集长度
s = []byte("你好世界")
r := bytes.Runes(s)
fmt.Println("转换前字符串的长度:", len(s)) //12"
fmt.Println("转换后字符串的长度:", len(r)) //4 utf8字符集查询长度
//字节链接
s2 := [][]byte{[]byte("你好"), []byte("世界")}
sep4 := []byte(",")
fmt.Println(string(bytes.Join(s2, sep4))) //你好,世界
sep5 := []byte("#")
fmt.Println(string(bytes.Join(s2, sep5))) //你好#世界
golang标准库errors
errors包实现了操作错误的函数。语言使用 eror 类型来返回函数执行过程中遇到的错误,如果返回的 error 值为ni1 ,则表示未遇到错误,否则 error 会返回一个字符串,用于说明遇到了什么错误。
error结构
type error interface {
Error() string
}
go你可以用任何类型去实现它 (只要添加一个 Error() 方法即可)也就是说,error 可以是位内大主,这心吓自,四
数返回的 error 值实际可以包含任意信息,不一定是字符串。error 不一定表示一个错误,它可以表示任何信息,比如 io 包中就用 error 类型的 io.EOF 表示数据读取结束,而不是遇到了什么错误。
errors 包实现了一个最简单的 error 类型,只包含一人字符串,它可以记录大多数情况下遇到的错误信息。errors包的用法也很简单,只有一个 New 函数,用于生成一个最简单的 error 对象:
func New(text string) error
import (
"errors"
"fmt"
)
func check(s string) (string, error) {
if s == "" {
err := errors.New("字符串不能为空")
return "", err
} else {
return s, nil
}
}
func main() {
s, err := check("hello")
if err != nil {
fmt.Printf("err: %v\n", err.Error())
} else {
fmt.Printf("s: %v\n", s)
}
}
标准库中的sort包
sort包提供了排序切片和用户自定义数据集的函数
sort包提供了排序切片和用户自定义数据集以及相关功能的函数
sort包主要针对 []int 、 []float64 、 []string 、以及其他自定义切片的排序,
结构体
type IntSlice struct
type Float64Slice
type StringSlice
函数
func Ints(a []int)
func IntsAreSorted(a []int) bool
func SearchInts(a []int, x int) int
func Float64s(a []float64)
func Float64sAreSorted(a []float64) bool
funcSearchFloat64s(a []float64,x float64) int
funcSearchFloat64s(a []flaot64,x float64) bool
func Strings(a []string)
func StringsAreSorted(a []string) bool
func SearchStrings(a []string,x string) int
func Sort(data Interface)
标准库 encoding/json
结构体转换为json
//结构体转换为json
type Person struct {
Name string
Age int
Email string
}
func Marshal() {
p := Person{
Name: "tom",
Age: 20,
Email: "[email protected]",
}
//结构体转换为json
b,_:= json.Marshal(p)
fmt.Printf("b: %v\n", string(b))
//json 转换为结构体
b1 := []byte(` {"Name":"tom","Age":20,"Email":"[email protected]"}`)
var m Person
json.Unmarshal(b1, &m)
fmt.Println(m)
//json 转换为结构体并输出
b3 := []byte(`{"Name":"tom","Age":20 ,"Email":"[email protected]","parents":["big tom","kite"]}`)
var f map[string]interface{}
json.Unmarshal(b3,&f)
fmt.Printf("f: %v\n", f)
for k, v := range f {
fmt.Printf("k: %v\n", k)
fmt.Printf("v: %v\n", v)
}
}
标准库math
该包包含一些常量和一些有用的数学计算函数,例如: 三角函数、随机数、绝对值、平方根等.
常量
fmt.Printf("float64的最大值是:%.f\n",math.MaxFloat64)
fmt.Printf("float64的最小值是:%.f\n",math.SmallestNonzeroFloat64)
fmt.Printf("float32的最大值是:%.f\n”,math.MaxFloat32)
fmt.Printf("float32的最小值是:%.fn",math.SmallestNonzeroFloat32)
fmt.Printf("Int8的最大值是:%d\n",math.MaxInt8)
fmt.Printf("Int8的最小值是 :%d\n”math .MinInt8)
fmt.Printf("Uint8的最大值是 :%d\n'math .MaxUint8)
fmt.Printf("Int16的最大值是 :%d\nmath.MaxInt16)
fmt.Printf("Int16的最小值是:%d\n"math .MinInt16)
fmt.Printf("Uint16的最大值是 :%d\nmath .MaxUint16
fmt.Printf("Int32的最大值是 :%d\n"math .MaxInt32)
fmt.Printf("Int32的最小值是 :%d\n"math.MinInt32)
fmt.Printf("Uint32的最大值是 :%d\n"math .MaxUint32
fmt.Printf("Int64的最大值是:%d\n"math .MaxInt64)
fmt.Printf("Int64的最小值是 :%d\n",math.MinInt64)
fmt.Printf("圆周率默认为 :%.2gf\n",math.Pi)
标准库 math/rand
rand包实现了伪随机数生成器。
随机数从资源生成。包水平的函数都使用的默认的公共资源。该资源会在程序每次运行时都产生确定的序列。如果需要每次运行产生不同的序列,应使用Seed函数进行初始化。默认资源可以安全的用于多go程并发。
func New(src Source) *Rand
func (r *Rand) Seed(seed int64)
func (r *Rand) Int() int
func (r *Rand) Int31() int32
func (r *Rand) Int63() int64
func (r *Rand) Uint32() uint32
func (r *Rand) Intn(n int) int
func (r *Rand) Int31n(n int32) int32
func (r *Rand) Int63n(n int64) int64
func (r *Rand) Float32() float32
func (r *Rand) Float64() float64
func (r *Rand) NormFloat64() float64
func (r *Rand) ExpFloat64() float64
func (r *Rand) Perm(n int) []int