C#/JAVA 程序員轉GO/GOLANG程序員筆記大全(DAY 06)

----------------------------------------- go 併發緩存

// 註解:go 語言天生爲程序併發所設計,能夠說go的強項就是在cpu併發上的處理。
// go 語言層面就支持了併發。(不是通常高級語言的多線程併發,是系統級真實併發)
// go 語言經過安全的通道發送和接受數據以實現同步
// 通常狀況下,一個普通的桌面計算機跑十幾二十幾個線程就有點負載過大了,可是一樣的硬件設備go能夠輕鬆上K。

 

----------------------------------------- goroutine安全

// 註解:go 併發設計的核心,goroutine在併發中起到的做用就是協程(CSP),可是它比線程更小。(協程=微線程)
// go 不支持後臺協程,意思就是主程序退出,協程跟着一塊兒退出
func newTask() {
    for {
        fmt.Println("new task ...")
        time.Sleep(time.Second) // 休眠1s
    }
}

func main() {
    go newTask()    // 新建一個協程,新建一個任務
    
    for {
        fmt.Println("main ...")
        time.Sleep(time.Second) // 休眠1s
    }
}
// result : 
// main... 
// new task ...
// ....

 

----------------------------------------- gosched多線程

// 註解:讓出CPU時間片,讓出當前 gorotine 的執行權限,
// 調度器安排其餘等待任務運行,並在下次某個時候從該位置恢復執行。
func main() {
    go func() {
        for i := 0; i < 5; i++ {
            fmt.Println("go")
        }
    }()
    
    for i := 0; i < 2; i++ {
        fmt.Println("hello")
    }
    
    // 這種狀況,匿名函數未獲得執行程序就結束了。    
    // 時間片案例演示代碼,修改以下:
    for i := 0 .... {
        runtime.Gosched()
        fmt.Println("hello")
    }
    // 執行結果:
    // go go ... hello ..
}


----------------------------------------- goexit併發

import "runtime"
// 註解:終止所在的協程 (所在的協程不是當前函數)
func test() {
    defer fmt.Println("ccccc")    
    runtime.Goexit()
    fmt.Println("dddd")
}

func main() {
    go func (){
        fmt.Println("aaaa")
        test()
        fmt.Println("bbbb")
    }
    for {
    }
    
    // result:
    // aaaa  cccc
}

 

----------------------------------------- gomaxProcs函數

// 註解:設置能夠並行計算的 CPU 核數的最大值
import "runtime"

func main() {
    n := runtime.GOMAXProcs(1)    //制定以1核運算
    fmt.Println("n = ", n)
    
    for {
        go fmt.Print(1)    
        fmt.Print(0)
    }
    
    // 打印結果:11111.. 一大片, 00000...一大片
    // 若是設置 GOMAXProcs(4) 爲 4 核交叉效果更好
}


----------------------------------------- 資源爭奪問題 channelspa

// 註解:channel 也是一種數據類型,同步
// 語法:channel <- value     // 發送 value 數據到 channel
// <- channel                 // 接收並丟棄

// 案例:
// 全局變量,建立一個 channel
var ch = make(chan int)

// 定義一個打印機,參數爲字符串,按每一個字符打印
func Printer(str string) {
    for _, data := range str {
        fmt.Printf("%c", data)
        time.Sleep(time.Second)
    }
    fmt.Printf("\n")
}

func person1() {
    Printer("loong print")
    ch <- 666        // 給管道寫數據
}

func person2() {
    <- ch            // 從管道取數據,若是管道沒有數據前他就會阻塞
    Printer("make print")
}

func main() {
    // 新建 2 個協程,表明 2 我的,2 我的同時使用打印機
    go person1()
    go person2()
}
// 註解: 【認真看】
// 沒有 channel 的狀況
// 打印結果混亂,person1 打印一個h,person2 打印一個 w,交叉了。不符合咱們的要求
// 增長 channel ,則在 <-ch 的地方進行了阻塞,經過進、出的方式融合這種解決這種併發互搶資源的問題。


----------------------------------------- channel 實現同步和數據交互線程

fun main() {
    ch := make(chan string)
    
    defer fmt.Println("主協程也結束")
    
    go func() {
        defer fmt.Println("子協程調用完畢。")
        
        for i := 0; i < 2; i++ {
            fmt.Println("子協程 i=", i)
            time.Sleep(time.Second)
        }
        
        ch <- "我是子協程,工做完畢"
    }
    
    str := <-ch // 沒有數據前,阻塞
    fmt.Println("str = ", str)
}

// 注意:程序需求:主程序結束以前,可以完整執行匿名函數中的代碼
// 使用 channel 配合完成


----------------------------------------- channel 無緩存&有緩存設計

c1 := make(chan int)         無緩衝
c2 := make(chan int,1)      有緩衝
c1 < -1                            
// 無緩衝:不單單是向 c1 通道放 1,
// 而是一直要等有別的協程 <-c1 接手了這個參數,那麼c1<-1纔會繼續下去,要否則就一直阻塞着。
// 有緩衝: c2<-1 則不會阻塞,由於緩衝大小是1(實際上是緩衝大小爲0),
// 只有當放第二個值的時候,第一個還沒被人拿走,這時候纔會阻塞。

// 不須要再使用記得關閉channel  close(c1)
// 判斷管道是否關閉 if num, ok := <- c1; ok == true { // 關閉了 }


----------------------------------------- channel 單方向
var ch1 chan int        // ch1 是一個正常的 channle,不是單向的
var ch2 chan<- float64    // ch2 是單向 channel,只用於寫 float64 數據
var ch3 <-chan int        // ch3 是單向 channel,只用於讀取 int 數據

// * 管道的操做,必定要避免死鎖的狀況。


----------------------------------------- channel 應用code

// 此案例能夠應用不少場景,每寫一個,則能夠消耗一個
// 此通道只能寫,不能讀
func producer(out chan<- int) {
    for i := 0; i < 10; i++ {
        out <- i * i
    }
    
    close(out)
}

// 此通道只能讀,不能寫
func consumer(in <-chan int) {
    for num := range in {
        fmt.Println("num = ", num)
    }
}

func main() {
    // 建立一個雙向通道
    ch := make(chan int)
    
    // 生產者,生產數字,寫入 channel
    // 開啓一個協程
    go producer(ch)
    
    // 消費者,從channel讀取內容打印
    consumer(ch)
}

 

----------------------------------------- Timer協程

import (
    "time"
    "fmt"
)

func main() {
    // 建立一個定時器,設置時間爲2s,2s後,往time通道寫內容
    timer := time.NewTimer(2 * time.Second)
    fmt.Println("當前時間:", time.Now())
    
    // 2s後,往timer.C寫數據,有數據後讀取
    t := <-time.C    // channel 沒有數據先後阻塞
    fmt.Println("t = ", t)
}


----------------------------------------- select

// 註解:go語言提供了一個關鍵字 select,經過 select 能夠監聽 channel 上的數據流動
// 語法:(相似 switch)
select {
    case <-chan:
        // 若是channel 成功讀到數據,則進入 case 塊語句
    case chan<- 1:
        // 若是channel 成功寫到數據,則進入 case 塊語句
    default:
        // 若是上面都沒有成功,則進入default處理流程
        // 注意:慎用,很消耗 cpu
}
相關文章
相關標籤/搜索