使用案例大白話講解Go語言並發編程go chan select close


使用案例大白話講解Go語言並發go chan select close

1、初識Go並發-go關鍵字實現多線程

使用go 關鍵字可以開辟一個新的協程(線程)線程之間並行執行

package main

import (
	"fmt"
	"time"
)

func  printData(data string)  { //循環打印數據
	for i:=0;i<5;i++{
		time.Sleep(time.Second)
		fmt.Println(data,":",i)//打印數據
	}
}


func main()  {

	printData("主程序")  //函數調用
	//主程序打印完了才會到下面的協程執行
	go  printData("子程序1") //多線程的並發
	go  printData("子程序2")
	go func(msg  string) {fmt.Println(msg)}("子程序3 Go!Go!Go!")//匿名函數風格

	//主程序一旦執行結束,子程序都會結束,所以這里睡10秒
	time.Sleep(time.Second*10)
}

運行結果如下:
主程序 : 0
主程序 : 1
主程序 : 2
主程序 : 3
主程序 : 4
子程序3 Go!
子程序1 : 0
子程序2 : 0
子程序2 : 1
子程序1 : 1
子程序1 : 2
子程序2 : 2
子程序2 : 3
子程序1 : 3
子程序1 : 4
子程序2 : 4

2、協程與協程之間的通信chan的使用

package main

import "fmt"

func main()  {
	messages:=make(chan string) //創建了一個通道。

	go func() {messages<-"Hello Chan!!"}()//發送數據給通道

	fmt.Println(messages) //消息通道內存地址

	msg:=<-messages  //從通道里面拿出字符串賦值給msg

	fmt.Println(msg)  

	messages2:=make(chan string,2)//通道接收多個消息,通道返回結果,2表示通道一次可以容納2條消息
	messages2<-"hello baby" //往通道里面放值
	messages2<-"hello Golang"
	fmt.Println(<-messages2)
	fmt.Println(<-messages2)
}


執行結果如下:
0xc000100060
Hello Chan!!
hello baby
hello Golang

3、使用協程與通道實現同步

package main

import "fmt"
import "time"

func worker(done chan bool){
	fmt.Println("開始干活")
	time.Sleep(time.Second*3) //干活
	fmt.Println("活干完了")

	done<-true  //返回數據,我們干完活了
}



func main()  {
	done := make(chan bool, 1) //創建一個通道,傳遞數據
	go worker(done) //用done接收消息
	<-done //鎖住,一直等待下去,直到接受到結果
	fmt.Println("game over")
}

執行結果如下:
開始干活
活干完了
game over

4、實現簡單的通信線路

package main
import "fmt"

//一個參數發送,一個參數用於接收
func send(sendStrings chan string, msg string)  {
	sendStrings<-msg //將msg放到發送的通道中
}

//一個參數發送,一個參數用於接收
func receive(sendStrings  chan string,receiveString chan string)  {
	msg := <-sendStrings
	receiveString <-msg
}
func main() {
	sendChan := make(chan  string ,1)
	receiveChan := make(chan  string ,1)//兩個通道

	send(sendChan,"發送一個消息:你好Chan")//調用
	receive(sendChan,receiveChan)
	fmt.Println(<-receiveChan)//顯示通信

}
執行結果如下:
發送一個消息:你好Chan

5、select的使用

簡單的介紹select的使用

package main

import (
	"fmt"
)

func main(){
	var  c1,c2,c3 chan int
	var  i1,i2 int
	c1 = make(chan int,1)
	c1 <- 10
	c3 = make(chan int,1)
	c3 <- 30
	//select 類似於switch語句,case滿足條件即可執行
	//注意 select 的 case 如果同一時間有多個條件滿足,會從滿足的條件中隨機一個執行,
	//例如以下例子偶爾執行結果是 received 30 from c3 偶爾結果是 received 10 from  c1
	select{
		case i1 = <- c1:
			fmt.Println("received",i1,"from  c1")
		case c2 <- i2:
			fmt.Println("send",i2,"to  c2")
		case i3,ok := (<- c3):
			if ok{
				fmt.Println("received",i3,"from c3")
			}else {
				fmt.Println("c3 is  closed")
			}
		default:
			fmt.Println("no  communication")
	}
	//time.Sleep(time.Second*3)
}

6、關於select的簡單的例子

package main

import "fmt"
import "time"

func main() {
	c1:=make(chan string) //選擇兩個通道
	c2:=make(chan string)
	go func() {
		time.Sleep(time.Second*3) //3秒
		c1<-"第一條信息"
	}()
	go func() {
		time.Sleep(time.Second*2) //2秒
		c2<-"第二條信息"
	}()
	//如果將此處的for循環中的2改為3會報一個錯誤"fatal error: all goroutines are asleep - deadlock!"
	//因此go語言會監測通道的生命周期 當協程執行結束不會有新信息往通道里面扔數據了那么goroutines 就會 asleep(睡眠)
	for  i:=0;i<2;i++{
		select{
			case msg1:= <-c1:
				fmt.Println("received",msg1)
			case msg2:=<-c2:
				fmt.Println("received",msg2)

		}
		fmt.Println("over!")
	}
}

執行結果如下:
received 第二條信息
over!
received 第一條信息
over!

7、select模擬實現訪問超時案例

package main

import "fmt"
import "time"

func main(){
	c1:=make(chan  string,1)//傳遞消息的通道
	go func() {
		time.Sleep(time.Second*2) //更改這里的值可以查看不同的效果
		c1<-"結果  num one"
		}()
	select {
		case res:= <-c1:
			fmt.Println(res)
		case  <-time.After(time.Second*3):
			fmt.Println("timeout 1")
	}

}

8、select實現阻塞與非阻塞接收信息

package main

import (
	"fmt"
	"time"
)

func main()  {
	messages :=make(chan string)
	signals:=make(chan bool)

	//非阻塞接收,有可以用就直接用,沒有就默認,不等待
	fmt.Println("程序開始執行...")
	go func() {
		time.Sleep(time.Second*2)
		messages<-"結果  num one"
	}()
	
	//如果存在default 那么就是非阻塞 有可以用就直接用,沒有就默認,不等待。
	//如果將下面的default語句塊注釋,那么select會等待接收messages中的值。
	select {
		case msg:= <-messages:
			fmt.Println("A 收到消息",msg)
		default:
			fmt.Println("C 沒有消息收到")

	}

	msg:="hi baby"
	select {
		case messages<-msg:
			fmt.Println("B sent message",msg)
		default:
			fmt.Println("B no message sent")
	}

	select {
		case msg:= <-messages:
			fmt.Println("C收到消息",msg)
		case sig := <-signals:
			fmt.Println("C收到消息",sig)
		default:
			fmt.Println("C no  activity")
	}
}

9、通道關閉 close

package main

import (
	"fmt"
	"time"
)

func main() {
	jobs :=make(chan int,5) //通道,返回結果數據
	done:=make(chan bool)//通道
	go func() {
		for {
			j,ok:= <-jobs
			//fmt.Printf("ok的類型為%T\n",ok) //ok的類型為bool 當通道關閉ok的類型為false
			if ok{
				fmt.Println("收到工作",j)
			} else {
				fmt.Println("收到全部工作結果")
				done <- true  //其實這里放true和false都無所謂
			}
		}
	}()
	for j:=1;j<=3;j++{
		time.Sleep(time.Second)
		jobs<-j
		fmt.Println("sent  job",j)
	}
	close(jobs)
	fmt.Println("發送完畢")
	//等待工作
	<-done
}

執行結果如下:
sent  job 1
收到工作 1
收到工作 2
sent  job 2
sent  job 3
發送完畢
收到工作 3
收到全部工作結果
收到全部工作結果

10、簡單實戰之工作池

package main

import "fmt"
import "time"
//程序目的:雇了三個工人給我打工,我發布工作,三個工人搶着干活。干完了把結果返回給我。jobs相當於是工作池
//工作協程,兩個通道,
//id編號,jobs工作編號,result  結果
//其實worker(id int,jobs<-chan int,result chan <-int) 中的<-都可以去掉 你也可以寫成worker(id int,jobs chan int,result chan int)
func worker(id int, jobs chan int, result chan int)  {
	for j:=range jobs{
		fmt.Println("worker",id,"開始干活",j)
		time.Sleep(time.Second)
		fmt.Println("worker",id,"結束干活",j)
		result <- j*10
	}
}


func main() {
	jobs := make(chan int,100) //工作
	result := make(chan int,100)//結果
	//開啟三個工作協程序,
	for w:=1; w<=3;w++{
		go worker(w,jobs,result)
	}
	for j:=1;j<=5;j++{
		jobs<-j //發布五個工作
	}
	close(jobs)//關閉jobs通道。

	for i:=1;i<=5;i++{
		fmt.Println("result:",<-result)
	}
}

程序執行結果:
worker 3 開始干活 1
worker 2 開始干活 3
worker 1 開始干活 2
worker 1 結束干活 2
worker 1 開始干活 4
result: 20
worker 3 結束干活 1
worker 2 結束干活 3
worker 2 開始干活 5
result: 10
result: 30
worker 1 結束干活 4
result: 40
worker 2 結束干活 5
result: 50


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM