Go基礎05

1 併發和並行

1 併發:同一時間段內,多個任務在執行(單個cpu,執行多個任務)
2 並行:同一時刻,多個任務在執行(多個cpu的支持)

2 goroutine

package main

import (
	"fmt"
	"runtime"
	"time"
)
//goroutine--->協程---2kb大小,100
//線程----》幾個m
//go協程會複用線程

// goroutine之間通訊,經過 信道channel 通訊
//go推崇用信道通訊,而不推崇用共享變量通訊(鎖,死鎖)

//啓動一個goroutine
func test()  {
	fmt.Println("go go go")
}


//	//go語言中,主線程不會等待goroutine執行完成,要等待它結束須要本身處理

// go 關鍵字開啓goroutine,一個goroutine只佔2kb,會自動擴容

/*
go語言的GMP模型
	-G:開的goroutine
	-M:M當成操做系統真正的線程,其實是用戶線程(用戶線程)
	-P:Processor:如今版本默認狀況是cpu核數(能夠當作cpu核數)
	用戶線程,操做系統線程
	python中,開的線程開出用戶線程,用戶線程跟操做系統線程1:1的對應關係
	某些語言,用戶線程和操做系統線程是n:1的關係
	go語言,用戶線程和操做系統線程是 n:m的關係
 */
func main() {
		//設置P的大小,認爲是cpu核數便可
		runtime.GOMAXPROCS(1)
		fmt.Println("主線程開始執行")
		go func() {
			for  {
				fmt.Println("xxxx")
			}
		}()
		//for i:=0;i<10;i++ {
		//	go func(){
		//		for  {
		//			fmt.Println("我是死循環")
		//
		//		}
		//
		//	}()
		//}
		time.Sleep(10*time.Second)
		fmt.Println("主線程結束執行")
}

3 信道(通道)

package main

import (
	"fmt"
	"time"
)

//不一樣goroutine之間通訊
//經過channel實現

func main() {
	//1 定義channel
	var c chan int
	//2 信道的零值(引用類型,空值爲nil,當作參數傳遞時,不須要取地址,改的就是原來的,須要初始化再使用)
	fmt.Println(c)
	//3 信道初始化
	c=make(chan int)  //數字暫時先不關注
	//4 信道的放值  (注意賦值和放值)
	//c<-1
	//c=12  //賦值報錯
	//5 信道取值
	//<-c
	//6 取出來賦值給一個變量 int
	//var a int
	//a=<-c
	//a:=<-c

	//7 信道默認無論放值仍是取值,都是阻塞的

	//c是引用類型
	go test1(c)

	a:=<-c  //阻塞   不但實現了兩條協程之間通訊,還實現了等待協程執行結束
	fmt.Println(a)

}

func test1(a chan int)  {
	fmt.Println("go go go ")
	time.Sleep(1*time.Second)
	//往信道中放一個值
	a<-10 //阻塞


}
package main

//信道小例子
//程序有一個數中 每一位的平方和與立方和,而後把平方和與立方和相加並打印出來

import (
	"fmt"
	"time"
)

func calcSquares(number int, squareop chan int) {
	sum := 0  //總和
	for number != 0 {
		digit := number % 10   //589對10取餘數,9   8   5
		sum += digit * digit  //sum=9*9   8*8     5*5
		number /= 10         //num=58    5       0
	}
	time.Sleep(2*time.Second)
	squareop <- sum
}

func calcCubes(number int, cubeop chan int) {
	sum := 0
	for number != 0 {
		digit := number % 10
		sum += digit * digit * digit
		number /= 10
	}
	time.Sleep(1*time.Second)
	cubeop <- sum
}

//func calcSquares(number int, squareop chan int) int{
//	sum := 0  //總和
//	for number != 0 {
//		digit := number % 10   //589對10取餘數,9   8   5
//		sum += digit * digit  //sum=9*9   8*8     5*5
//		number /= 10         //num=58    5       0
//	}
//	time.Sleep(2*time.Second)
//	return sum
//}
//
//func calcCubes(number int, cubeop chan int) int{
//	sum := 0
//	for number != 0 {
//		digit := number % 10
//		sum += digit * digit * digit
//		number /= 10
//	}
//	time.Sleep(2*time.Second)
//	return sum
//}

func main() {
	ctime:=time.Now().Unix()
	fmt.Println(ctime)
	number := 589
	sqrch := make(chan int)
	cubech := make(chan int)
	//num1:=calcSquares(number, sqrch)
	//num2:=calcCubes(number, cubech)
	go calcSquares(number, sqrch)
	go calcCubes(number, cubech)
	squares, cubes := <-sqrch, <-cubech
	//squares:= <-sqrch
	//cubes:=<-cubech
	fmt.Println("Final output", squares + cubes)
	ttime:=time.Now().Unix()
	fmt.Println(ttime)
	fmt.Println(ttime-ctime)
}
package main

import "fmt"

//信道的死鎖現象,默認都是阻塞的,一旦有一個放,沒有人取  或者一我的取,沒有人放,就會出現死鎖

//func main() {
//	//var c chan int =make(chan int )
//
//	//c<-1  //其實放不進去,阻塞在這,就死鎖了
//	//<-c     //沒有,取不到,阻塞在這,就死鎖了
//
//}


//單向信道
//func sendData(sendch chan<- int) {
//	sendch <- 10
//}
//
//func main() {
//	//sendch := make(chan<- int)   //定義了一個只寫信道
//	sendch := make(chan int)   //定義了一個可讀可寫信道
//	go sendData(sendch)        //傳到函數中轉成只寫信道,在goroutine中,只負責寫,不能往外讀,主協程讀
//	fmt.Println(<-sendch)   //只寫信道一旦讀,就有問題
//}



///3 關閉信道
//func main() {
//	sendch := make(chan int)
//	//關閉信道
//	//close(sendch)
//
//	//信道能夠用for循環循環
//}


// 信道關閉close(sendch) ,for循環循環信道,若是不關閉會報死鎖,若是關閉了,放不進去,循環結束
func producer(chnl chan int) {
	for i := 0; i < 100; i++ {
		fmt.Println("放入了",i)
		chnl <- i
	}
	close(chnl)
}
func main() {
	ch := make(chan int)
	go producer(ch)
	for v := range ch {
		fmt.Println("Received ",v)
	}
}

4 緩衝信道

package main

import (
	"fmt"
	"sync"
	"time"
)
//緩衝信道:只在緩衝已滿的狀況,纔會阻塞向緩衝信道,只有在緩衝爲空的時候,纔會阻塞從緩衝信道接收數據

func main() {
var c chan int =make(chan int,6)  //無緩衝信道數字是0

2 長度 vs 容量
//fmt.Println(len(c))  //目前放了多少
//fmt.Println(cap(c)) //能夠最多放多少
3 WaitGroup  等待全部goroutine執行完成
4  使用信道如何實現?
func process1(i int,wg *sync.WaitGroup)  {
	fmt.Println("started Goroutine ", i)
	time.Sleep(2 * time.Second)
	fmt.Printf("Goroutine %d ended\n", i)
	//一旦有一個完成,減一
	wg.Done()
}
func main() {
	var wg sync.WaitGroup   //沒有初始化,值類型,當作參數傳遞,須要取地址
	//fmt.Println(wg)
	for i:=0;i<10;i++ {
		wg.Add(1) //啓動一個goroutine,add加1
		go process1(i,&wg)
	}
	wg.Wait() // 一直阻塞在這,知道調用了10個done,計數器減到零
}
   var wg sync.WaitGroup   //沒有初始化,值類型,當作參數傳遞,須要取地址
    wg.Add(1)
    wg.Done()
    wg.Wait()

5 Select

package main

import (
	"fmt"
	"time"
)
// 隨機選取
func server1(ch chan string) {
	ch <- "from server1"
}
func server2(ch chan string) {
	ch <- "from server2"

}
func main() {
	output1 := make(chan string)
	output2 := make(chan string)
	go server1(output1)
	go server2(output2)
	time.Sleep(1 * time.Second)
    for {
	select {
       time.Sleep(1 * time.Second)
	case s1 := <-output1:
		fmt.Println(s1)
	case s2 := <-output2:
		fmt.Println(s2)
        default:
        ...(作其餘事)
	}
    }
}

6 mutex

package main

import (
	"fmt"
	"sync"
)
var m sync.Mutex
m.Lock()
m.Unlock() //是個值類型,函數傳遞須要傳地址
// 使用鎖的場景:多個goroutine經過共享內存在實現數據通訊
// 臨界區:當程序併發地運行時,多個 [Go 協程]同時修改共享資源的代碼。這些修改共享資源的代碼稱爲臨界區。
//若是在任意時刻只容許一個 Go 協程訪問臨界區,那麼就能夠避免競態條件。而使用 Mutex 能夠達到這個目的
channel 來作
	ch := make(chan bool, 1) 
	ch <- true  // 緩衝信道放滿了,就會阻塞
	x = x + 1
	<- ch
// 不一樣goroutine之間傳遞數據:共享變量,  經過信道
// 若是是修改共享變量,建議加鎖
//若是是協程之間通訊,用信道

7 異常處理

defer ...   //壓棧出棧 執行順序 先定義的最後執行,釋放資源
recover() 恢復
panic 主動報錯
test(){
    def func(){
        if e:=recover();e!=nil{
            fmt.Println(e)
        }
        //沒錯走這裏 相似於finally 寫個return 
    }()  
}
相關文章
相關標籤/搜索