每日播报!goLang包以及并发编程

2022-12-14 19:21:51 来源:51CTO博客

1 包

包可以区分命名空间,一个文件夹中不能有两个同名文件,go中创建一个包一般是创建一个文件夹,在该文件夹里面的go文件中使用关键字package声明包名称,通常文件夹名称和包名称相同,并且一个文件夹下面只有一个包

创建包

创建一个名为dao的文件夹


【资料图】

创建一个dao.go文件

在该文件中声明包

package daoimport "fmt"func Test1(){    fmt.PrintLn("test package")}

导入包

要使用某个包下面的方法或者变量 需要导入该包,导入需要从GOPATH开始包路径,例如在service.go中导入dao包

package mainimport "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 mainimport("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 mainimport (    "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 mainimport (   "fmt"   "sync")var wp sync.WaitGroupfunc 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 mainimport (    "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 mainimport (    "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 mainimport (    "fmt"    "sync")var i int = 100var wg sync.WaitGroupvar lock sync.Mutexfunc 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 mainimport "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 mainimport (    "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 mainimport (    "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 mainimport (    "fmt"    "sync/atomic"    "time")var i int32 = 100func 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 mainimport (    "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();}
// 跌得当前正在运行的速程1dfmt.Printf("os.Getpid(): sw"n". os.Getpid())// 父idfmt.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)

标准库中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() //清空环境变量

标准库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 和 ioWriterstrings.Reader 实现了 io.Readerbufio.Reader/Writer 分别实现了 io.Reader 和 io.Writerbytes.Buffer 同时实现了 io.Reader 和 io.Writerbytes.Reader 实现了 io.Readercompress/gzip.Reader/Writer 分别实现了 io.Reader 和 io.Writercrypto/cipher,StreamReader/StreamWriter 分别实现了 o.Reader 和 io.Writercrypto/tls.Conn 同时实现了 io.Reader 和 io.Writerencoding/csv.Reader/Writer 分别实现了 io.Reader 和 io.Writer

标准库ioutil包

封装一些实用的1/0 函教ReadAll 读取数据,返回读到的字节 sliceReadDir 读取一个目录,返回目录入口数组 Dos.FilelnfoReadFile 读一个文件,返回文件内容(字节slice)WriteFile 根据文件路径,写入字节sliceTempDir 在一个目录中创建指定前缓名的临时目录,返回新临时目录的路径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))

标准库 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的内容

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的指针。

标签: 环境变量 抛出异常

上一篇:Ubuntu中使用gcc/g++编译C/C++
下一篇:讯息:【Python多任务--进程,协程】