使用案例大白話講解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