亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Go語言并發(fā)之Select多路選擇操作符用法詳解

 更新時間:2023年06月12日 10:22:26   作者:242030  
Go?語言借用多路復(fù)用的概念,提供了?select?關(guān)鍵字,用于多路監(jiān)聽多個通道,本文就來和大家聊聊Go語言中Select多路選擇操作符的具體用法,希望對大家有所幫助

1、Go語言并發(fā)之Select多路選擇操作符

select 是類 UNIX 系統(tǒng)提供的一個多路復(fù)用系統(tǒng) API,Go 語言借用多路復(fù)用的概念,提供了 select 關(guān)鍵字,用于多路監(jiān)聽多個通道。當(dāng)監(jiān)聽的通道沒有狀態(tài)是可讀或可寫的,select 是阻塞的;只要監(jiān)聽的通道中有一個狀態(tài)是可讀或可寫,則 select 就不會阻寒,而是進(jìn)入處理就緒通道的分支流程。如果監(jiān)聽的通道有多個口讀或口寫的狀態(tài),則 select 隨利選取一個處理。

package main
func main() {
	ch := make(chan int, 1)
	go func(chan int) {
		for {
			select {
			// 0或者1寫入是隨機(jī)的
			case ch <- 0:
			case ch <- 1:
			}
		}
	}(ch)
	for i := 0; i < 10; i++ {
		println(<-ch)
	}
}

輸出

# 程序結(jié)果
1
1
1
1
0
0
0
0
1
1

1.1 多路選擇操作符 select

在golang語言中,select 語句就是用來監(jiān)聽和channel有關(guān)的IO操作,當(dāng)IO操作發(fā)生時,觸發(fā)相應(yīng)的case動作。

有了select語句,可以實(shí)現(xiàn) main 主線程與 goroutine 線程之間的互動。

select使用時類似 switch-case 的用法,適用于處理多通道的場景,會通過類似 are-you-ready-polling 的機(jī)制來工作。

select {
    case <-ch1 :     // 檢測有沒有數(shù)據(jù)可讀
        // 一旦成功讀取到數(shù)據(jù),則進(jìn)行該case處理語句
    case ch2 <- 1 :  // 檢測有沒有數(shù)據(jù)可寫
        // 一旦成功向ch2寫入數(shù)據(jù),則進(jìn)行該case處理語句
    default:
        // 如果以上都沒有符合條件,那么進(jìn)入default處理流程
}

select 語句只能用于 channel 信道的IO操作,每個 case 都必須是一個信道。

如果不設(shè)置 default 條件,當(dāng)沒有IO操作發(fā)生時,select 語句就會一直阻塞。

如果有一個或多個IO操作發(fā)生時,Go運(yùn)行時會隨機(jī)選擇一個 case 執(zhí)行,但此時將無法保證執(zhí)行順序。

對于 case 語句,如果存在信道值為 nil 的讀寫操作,則該分支將被忽略,可以理解為相當(dāng)于從select語句中刪除了這個case;

對于空的 select 語句,會引起死鎖;

對于在 for中的select語句,不能添加 default,否則會引起cpu占用過高的問題;

隨機(jī)性:多個 case 之間并非順序的,遵循「先到先執(zhí)行,同時到則隨機(jī)執(zhí)行」的原則。

一次性:和 switch-case 一樣,select-case也只會執(zhí)行一次,如果需要多次處理,需要在外層套一個循環(huán)。

default 不會阻塞,會一直執(zhí)行,當(dāng)與 for 循環(huán)組合使用時可能出現(xiàn)死循環(huán)。

1.2 阻塞與非阻塞 select

select 默認(rèn)是阻塞的,當(dāng)沒有 case 處于激活狀態(tài)時,會一直阻塞住,極端的甚至可以這樣用:

package main
func main() {
	select {
	// 啥也不干,一直阻塞住
	}
}

執(zhí)行后,引發(fā)死鎖,打印如下:

# 輸出
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [select (no cases)]:

通過增加 default,可以實(shí)現(xiàn)非阻塞的 select:

select {
    case x, ok := <-ch1:
        ...
    case ch2 <- y:
        ...
    default:
        fmt.Println("default")
}

1.3 多 case 與 default 執(zhí)行的順序

整體流程如圖所示:

1.4 多個IO操作發(fā)生時,case語句是隨機(jī)執(zhí)行的

package main
import "fmt"
func main() {
	// 創(chuàng)建一個長度帶緩沖的整型通道
	ch1 := make(chan int, 1)
	// 向通道中寫入數(shù)據(jù)
	ch1 <- 1
	ch2 := make(chan int, 1)
	ch2 <- 2
	select {
	case <-ch1:
		fmt.Println("ch1 read")
	case <-ch2:
		fmt.Println("ch2 read")
	}
}

多次執(zhí)行后,會隨機(jī)打印 ch1 read 或 ch2 read。

1.5 for中的select 引起CPU資源消耗過高

package main
import (
	"fmt"
	"time"
)
func main() {
	quit := make(chan bool)
	go func() {
		for {
			select {
			case <-quit:
				fmt.Println("quit")
				// 使用 return 就會退出整個goroutine線程;如果使用 break,程序仍然在for循環(huán)中執(zhí)行
				return
			default:
				fmt.Println("default")
			}
		}
	}()
	time.Sleep(3 * time.Second)
	quit <- true // 主線程在3秒后,向quit信道寫入數(shù)據(jù)
	time.Sleep(2 * time.Second)
	fmt.Println("main")
}

輸出:

# 程序結(jié)果
default
default
default
default
default
default
default
......
......
default
default
default
default
default
quit
main

在 for{} 的 select 語句中使用了 default 后,線程就會無限執(zhí)行 default 條件,直到 quit 信道中讀到數(shù)據(jù),否則會一直在一個死循環(huán)中運(yùn)行,從而導(dǎo)致占滿整個CPU資源。

在 for{} 的 select 語句中,不建議使用 default 條件。

1.6 select語句的實(shí)際應(yīng)用

(1)、實(shí)現(xiàn) main主線程與 goroutine線程之間的交互、通信

package main
import (
	"bufio"
	"fmt"
	"os"
)
// 通過控制臺輸入"bye",來控制main函數(shù)結(jié)束運(yùn)行
func main() {
	quit := make(chan bool)
	ch := make(chan string)
	go func() {
		for {
			select {
			case name := <-ch:
				fmt.Printf("from main msg: [%v]\n", name)
				if name == "bye" {
					quit <- true
				} else {
					quit <- false
				}
			}
		}
	}()
	for {
		// 控制臺輸入
		fmt.Print("please input string: ")
		scanner := bufio.NewScanner(os.Stdin)
		scanner.Scan()
		ch <- scanner.Text()
		isOver := <-quit
		if isOver {
			break
		}
	}
	fmt.Println("main over")
}

輸出:

please input string: from main msg: [ttttt]
please input string: from main msg: [qqqq]
please input string: from main msg: [wwww]
please input string: from main msg: [bye]
main over

(2)、超時實(shí)現(xiàn)

package main
import (
	"fmt"
	"time"
)
func main() {
	quit := make(chan bool)
	ch := make(chan int)
	go func() {
		for {
			select {
			case num := <-ch:
				fmt.Println("num = ", num)
			case <-time.After(5 * time.Second):
				fmt.Println("超時")
				quit <- true
			}
		}
	}()
	for i := 0; i < 2; i++ {
		ch <- i
		time.Sleep(time.Second)
	}
	<-quit // 等待超時后, 結(jié)束 main主線程
	fmt.Println("程序結(jié)束")
}

輸出:

num =  0
num =  1
超時
程序結(jié)束

1.7 select使用的區(qū)別

package main
import (
	"fmt"
	"time"
)
func server1(ch chan string) {
	time.Sleep(6 * time.Second)
	ch <- "from server1"
}
func server2(ch chan string) {
	time.Sleep(3 * time.Second)
	ch <- "from server2"
}
func main() {
	output1 := make(chan string)
	output2 := make(chan string)
	go server1(output1)
	go server2(output2)
	s1 := <-output1
	fmt.Println(s1)
	s2 := <-output2
	fmt.Println(s2)
}

程序結(jié)果

from server1
from server2

package main
import (
	"fmt"
	"time"
)
func server1(ch chan string) {
	time.Sleep(6 * time.Second)
	ch <- "from server1"
}
func server2(ch chan string) {
	time.Sleep(3 * time.Second)
	ch <- "from server2"
}
func main() {
	output1 := make(chan string)
	output2 := make(chan string)
	go server1(output1)
	go server2(output2)
	select {
	case s1 := <-output1:
		fmt.Println(s1)
	case s2 := <-output2:
		fmt.Println(s2)
	}
}

程序結(jié)果

from server2

package main
import "time"
import (
	"fmt"
)
// select 管道參數(shù)并行
func server1(ch chan string) {
	time.Sleep(time.Second * 6)
	ch <- "response from server1"
}
func server2(ch chan string) {
	time.Sleep(time.Second * 3)
	ch <- "response from server2"
}
func main() {
	output1 := make(chan string)
	output2 := make(chan string)
	go server1(output1)
	go server2(output2)
	// 管道同時ready,select隨機(jī)執(zhí)行
	// time.Sleep(time.Second)
	select {
	case s1 := <-output1:
		fmt.Println("s1:", s1)
	case s2 := <-output2:
		fmt.Println("s2:", s2)
	default:
		fmt.Println("run default")
	}
}

程序結(jié)果

run default

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)
	select {
	case s1 := <-output1:
		fmt.Println(s1)
	case s2 := <-output2:
		fmt.Println(s2)
	}
}

輸出

from server2 和 from server1 隨機(jī)交替

以上就是Go語言并發(fā)之Select多路選擇操作符用法詳解的詳細(xì)內(nèi)容,更多關(guān)于Go Select的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go程序的init函數(shù)在什么時候執(zhí)行

    Go程序的init函數(shù)在什么時候執(zhí)行

    在Go語言中,init?函數(shù)是一個特殊的函數(shù),它用于執(zhí)行程序的初始化任務(wù),本文主要介紹了Go程序的init函數(shù)在什么時候執(zhí)行,感興趣的可以了解一下
    2023-10-10
  • golang redis中Pipeline通道的使用詳解

    golang redis中Pipeline通道的使用詳解

    本文主要介紹了golang redis中Pipeline通道的使用詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Golang實(shí)現(xiàn)加權(quán)輪詢負(fù)載均衡算法

    Golang實(shí)現(xiàn)加權(quán)輪詢負(fù)載均衡算法

    加權(quán)輪詢負(fù)載均衡算法是一種常見的負(fù)載均衡策略,本文主要介紹了Golang實(shí)現(xiàn)加權(quán)輪詢負(fù)載均衡算法,具有一定的參考價值,感興趣的可以了解一下
    2024-08-08
  • Golang?中判斷兩個結(jié)構(gòu)體相等的方法

    Golang?中判斷兩個結(jié)構(gòu)體相等的方法

    這篇文章主要介紹了Golang?中如何判斷兩個結(jié)構(gòu)體相等,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-08-08
  • GoFrame?gredis緩存DoVar及Conn連接對象的自動序列化

    GoFrame?gredis緩存DoVar及Conn連接對象的自動序列化

    這篇文章主要為大家介紹了GoFrame?gredis干貨DoVar?Conn連接對象自動序列化詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • golang替換無法顯示的特殊字符(\u0000,?\000,?^@)

    golang替換無法顯示的特殊字符(\u0000,?\000,?^@)

    這篇文章主要介紹了golang替換無法顯示的特殊字符,包括的字符有\(zhòng)u0000,?\000,?^@等,下文詳細(xì)資料,需要的小伙伴可以參考一下
    2022-04-04
  • Go中strings包的基本使用示例代碼

    Go中strings包的基本使用示例代碼

    本文詳細(xì)介紹了Go語言中strings包的基本使用方法,包括字符串的前綴、后綴判斷,字符串包含、索引查找、字符串替換、計(jì)數(shù)、重復(fù)、大小寫轉(zhuǎn)換、修剪、分割、拼接以及數(shù)據(jù)類型轉(zhuǎn)換等功能,示例代碼豐富,適合初學(xué)者和需要使用字符串處理功能的開發(fā)者參考學(xué)習(xí)
    2024-10-10
  • Go語言實(shí)現(xiàn)類似c++中的多態(tài)功能實(shí)例

    Go語言實(shí)現(xiàn)類似c++中的多態(tài)功能實(shí)例

    Go本身不具有多態(tài)的特性,不能夠像Java、C++那樣編寫多態(tài)類、多態(tài)方法。但是,使用Go可以編寫具有多態(tài)功能的類綁定的方法。下面來一起看看吧
    2016-09-09
  • Go語言中日志統(tǒng)一處理詳解

    Go語言中日志統(tǒng)一處理詳解

    在現(xiàn)代軟件開發(fā)中,日志記錄是一項(xiàng)至關(guān)重要的任務(wù),它不僅幫助開發(fā)人員診斷問題,還有助于監(jiān)控和維護(hù)應(yīng)用程序,本文主要來和大家聊聊日志的統(tǒng)一處理,感興趣的小伙伴可以了解下
    2024-01-01
  • golang語言如何將interface轉(zhuǎn)為int, string,slice,struct等類型

    golang語言如何將interface轉(zhuǎn)為int, string,slice,struct等類型

    這篇文章主要介紹了golang語言如何將interface轉(zhuǎn)為int, string,slice,struct等類型,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-12-12

最新評論