每日播報!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多任務--進程,協程】