自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

深入Go原理:協(xié)程間通信基礎(chǔ)Chan

開(kāi)發(fā) 前端
當(dāng) select? 語(yǔ)句等待多個(gè)通道時(shí),如果其中一個(gè)通道操作可以進(jìn)行,其它通道的操作不會(huì)繼續(xù)等待,而是等待下一次 select? 語(yǔ)句的評(píng)估。

在 Go 語(yǔ)言中,chan(通道)是用于在不同 goroutine 之間進(jìn)行通信和同步的重要機(jī)制。它的設(shè)計(jì)和實(shí)現(xiàn)允許在并發(fā)編程中安全、有效地傳遞數(shù)據(jù)。以下是 chan 的工作原理和實(shí)現(xiàn)細(xì)節(jié)

基本概念

通道類型

通道有類型,指定了通道能夠傳遞的數(shù)據(jù)類型。例如,chan int 是一個(gè)只能傳遞整數(shù)的通道。

無(wú)緩沖通道

沒(méi)有緩沖區(qū)的通道,發(fā)送和接收操作是同步的,即發(fā)送操作會(huì)阻塞直到有接收操作發(fā)生。

有緩沖通道

具有一定緩沖區(qū)的通道,發(fā)送操作在緩沖區(qū)未滿時(shí)不會(huì)阻塞,直到緩沖區(qū)滿時(shí)才會(huì)阻塞。

通道的內(nèi)部結(jié)構(gòu)

通道在內(nèi)部是通過(guò) hchan 結(jié)構(gòu)體來(lái)實(shí)現(xiàn)的。這個(gè)結(jié)構(gòu)體包含了通道的基本信息和狀態(tài)

type hchan struct {
    qcount   uint           // 緩沖區(qū)中數(shù)據(jù)的數(shù)量
    dataqsiz uint           // 緩沖區(qū)的大小
    buf      unsafe.Pointer // 緩沖區(qū)指針
    elemsize uint16         // 元素的大小
    closed   uint32         // 通道是否關(guān)閉
    sendx    uint           // 發(fā)送操作的索引
    recvx    uint           // 接收操作的索引
    recvq    waitq          // 等待接收的 goroutine 隊(duì)列
    sendq    waitq          // 等待發(fā)送的 goroutine 隊(duì)列
    lock     mutex          // 保護(hù)通道的互斥鎖
}

發(fā)送和接收操作

無(wú)緩沖通道

發(fā)送操作

如果沒(méi)有接收者,發(fā)送方會(huì)阻塞,直到有接收方開(kāi)始接收。

接收操作

如果沒(méi)有發(fā)送者,接收方會(huì)阻塞,直到有發(fā)送方開(kāi)始發(fā)送。

有緩沖通道

發(fā)送操作

如果緩沖區(qū)未滿,數(shù)據(jù)直接寫(xiě)入緩沖區(qū)。若緩沖區(qū)已滿,發(fā)送方會(huì)阻塞,直到有空間可用。

接收操作

如果緩沖區(qū)不為空,數(shù)據(jù)直接從緩沖區(qū)讀取。若緩沖區(qū)為空,接收方會(huì)阻塞,直到有數(shù)據(jù)可讀。

通道的同步機(jī)制

通道的發(fā)送和接收操作都是原子性的,并且由互斥鎖保護(hù)。這確保了多個(gè) goroutine 同時(shí)操作通道時(shí)不會(huì)發(fā)生競(jìng)態(tài)條件。

互斥鎖(Mutex)

每個(gè)通道都有一個(gè)互斥鎖,用于保護(hù)通道的狀態(tài)和數(shù)據(jù)。

等待隊(duì)列(Wait Queue)

通道維護(hù)兩個(gè)等待隊(duì)列,一個(gè)用于等待接收的 goroutine,一個(gè)用于等待發(fā)送的 goroutine。當(dāng)發(fā)送或接收操作不能立即完成時(shí),goroutine 會(huì)被加入相應(yīng)的等待隊(duì)列中。

通道關(guān)閉

關(guān)閉通道

通過(guò)調(diào)用 close(chan) 可以關(guān)閉通道。關(guān)閉操作會(huì)設(shè)置通道的 closed 標(biāo)志,并喚醒所有在通道上阻塞的發(fā)送和接收操作。

關(guān)閉后的操作

向已關(guān)閉的通道發(fā)送數(shù)據(jù)會(huì)引發(fā) panic,從已關(guān)閉的通道接收數(shù)據(jù)會(huì)立即返回零值。

實(shí)現(xiàn)細(xì)節(jié)

以下是通道發(fā)送和接收操作的一些實(shí)現(xiàn)細(xì)節(jié)

發(fā)送操作

chan send 檢查通道是否關(guān)閉,如果沒(méi)有接收者且緩沖區(qū)未滿,數(shù)據(jù)會(huì)被直接寫(xiě)入緩沖區(qū),否則會(huì)阻塞當(dāng)前 goroutine 并將其加入 sendq。

接收操作

chan recv 檢查通道是否關(guān)閉或緩沖區(qū)是否為空,如果有數(shù)據(jù)則直接返回,否則阻塞當(dāng)前 goroutine 并將其加入 recvq。

總結(jié)

Go 語(yǔ)言中的通道通過(guò)上述機(jī)制實(shí)現(xiàn)了 goroutine 之間的安全、高效通信。通道的設(shè)計(jì)考慮了并發(fā)編程中的同步問(wèn)題,通過(guò)緩沖機(jī)制和等待隊(duì)列的管理,使得數(shù)據(jù)傳遞和同步操作都能高效地進(jìn)行。

例子

在 Go 語(yǔ)言中,可以通過(guò) make 函數(shù)來(lái)定義通道。根據(jù)是否指定緩沖區(qū)大小,可以創(chuàng)建無(wú)緩沖區(qū)通道和有緩沖區(qū)通道。以下是具體的定義和示例:

無(wú)緩沖區(qū)通道

無(wú)緩沖區(qū)通道是指在沒(méi)有緩沖區(qū)的情況下,發(fā)送和接收操作是同步的。發(fā)送操作會(huì)一直阻塞,直到有接收者接收數(shù)據(jù)。

定義無(wú)緩沖區(qū)通道
ch := make(chan int)
示例
package main
import (
    "fmt"
)
func main() {
    ch := make(chan int)
    // 啟動(dòng)一個(gè) goroutine 發(fā)送數(shù)據(jù)
    go func() {
        ch <- 42 // 發(fā)送操作會(huì)阻塞,直到有接收者
    }()
    // 接收數(shù)據(jù)
    value := <-ch
    fmt.Println(value) // 輸出: 42
}

在這個(gè)例子中,ch 是一個(gè)無(wú)緩沖區(qū)通道,發(fā)送操作 ch <- 42 會(huì)阻塞,直到主 goroutine 執(zhí)行 <-ch 接收數(shù)據(jù)。

有緩沖區(qū)通道

有緩沖區(qū)通道允許在緩沖區(qū)未滿時(shí)發(fā)送操作不會(huì)阻塞,直到緩沖區(qū)滿時(shí)才會(huì)阻塞。

定義有緩沖區(qū)通道
ch := make(chan int, 3) // 創(chuàng)建一個(gè)緩沖區(qū)大小為 3 的通道
示例
package main
import (
    "fmt"
)
func main() {
    ch := make(chan int, 3) // 定義緩沖區(qū)大小為 3 的通道
    // 發(fā)送數(shù)據(jù)到通道,不會(huì)阻塞
    ch <- 1
    ch <- 2
    ch <- 3
    // 緩沖區(qū)已滿,下面的發(fā)送操作會(huì)阻塞,直到有接收者
    go func() {
        ch <- 4
    }()
    // 接收數(shù)據(jù)
    fmt.Println(<-ch) // 輸出: 1
    fmt.Println(<-ch) // 輸出: 2
    fmt.Println(<-ch) // 輸出: 3
    fmt.Println(<-ch) // 輸出: 4
}

在這個(gè)例子中,ch 是一個(gè)有緩沖區(qū)通道,緩沖區(qū)大小為 3。前 3 個(gè)發(fā)送操作不會(huì)阻塞,直到緩沖區(qū)滿后,第 4 個(gè)發(fā)送操作會(huì)阻塞,直到有接收者開(kāi)始接收數(shù)據(jù)。

總結(jié)

通過(guò) make(chan T) 可以創(chuàng)建無(wú)緩沖區(qū)通道,通過(guò) make(chan T, capacity) 可以創(chuàng)建有緩沖區(qū)通道。無(wú)緩沖區(qū)通道在發(fā)送和接收操作上是同步的,而有緩沖區(qū)通道允許在緩沖區(qū)未滿時(shí)進(jìn)行非阻塞的發(fā)送操作。通過(guò)以上示例,可以清晰地看到兩種通道的行為差異。

select

在 Go 語(yǔ)言中,select 語(yǔ)句用于處理多個(gè)通道的通信操作。它的作用是讓 goroutine 可以同時(shí)等待多個(gè)通道操作(發(fā)送或接收),并在其中任何一個(gè)通道操作完成時(shí)執(zhí)行相應(yīng)的分支代碼。select 語(yǔ)句的使用使得在處理并發(fā)編程時(shí)更加靈活和高效。

select 語(yǔ)句的基本用法

select 語(yǔ)句的語(yǔ)法與 switch 語(yǔ)句類似,但它專門(mén)用于通道操作。每個(gè) case 分支包含一個(gè)通道操作(發(fā)送或接收),select 會(huì)選擇其中一個(gè)已準(zhǔn)備好的通道操作進(jìn)行處理。

語(yǔ)法結(jié)構(gòu)

select {
case expr1:
    // 如果 expr1 通道操作可以進(jìn)行,則執(zhí)行此分支
case expr2:
    // 如果 expr2 通道操作可以進(jìn)行,則執(zhí)行此分支
default:
    // 如果沒(méi)有任何通道操作可以進(jìn)行,則執(zhí)行此分支
}

示例:使用 select 同時(shí)等待多個(gè)通道操作

以下是一個(gè)使用 select 語(yǔ)句的示例:

package main
import (
    "fmt"
    "time"
)
func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    // 啟動(dòng)第一個(gè) goroutine
    go func() {
        time.Sleep(2 * time.Second)
        ch1 <- "message from ch1"
    }()
    // 啟動(dòng)第二個(gè) goroutine
    go func() {
        time.Sleep(1 * time.Second)
        ch2 <- "message from ch2"
    }()
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println(msg1)
        case msg2 := <-ch2:
            fmt.Println(msg2)
        }
    }
}

在這個(gè)例子中,有兩個(gè)通道 ch1 和 ch2,每個(gè)通道都在不同的 goroutine 中發(fā)送消息。select 語(yǔ)句使得主 goroutine 可以同時(shí)等待兩個(gè)通道的消息,并在任意一個(gè)通道接收到消息時(shí)執(zhí)行相應(yīng)的分支。

default 分支

如果在 select 語(yǔ)句中添加了 default 分支,當(dāng)所有通道操作都無(wú)法立即進(jìn)行時(shí),會(huì)執(zhí)行 default 分支。這樣可以避免 select 語(yǔ)句阻塞。

示例:帶有 default 分支的 select

package main
import (
    "fmt"
    "time"
)
func main() {
    ch := make(chan string)
    go func() {
        time.Sleep(2 * time.Second)
        ch <- "message"
    }()
    for {
        select {
        case msg := <-ch:
            fmt.Println(msg)
            return
        default:
            fmt.Println("No message received, doing other work")
            time.Sleep(500 * time.Millisecond)
        }
    }
}

在這個(gè)例子中,如果通道 ch 上沒(méi)有消息可接收,select 會(huì)執(zhí)行 default 分支,打印一條消息并繼續(xù)執(zhí)行其他工作。

總結(jié)

select 語(yǔ)句是 Go 語(yǔ)言中處理并發(fā)編程的重要工具,通過(guò)它可以同時(shí)等待多個(gè)通道操作并在其中一個(gè)操作完成時(shí)進(jìn)行相應(yīng)處理。select 提供了一種靈活且高效的方式來(lái)處理多個(gè)通道之間的通信,使得并發(fā)程序的設(shè)計(jì)更加簡(jiǎn)潔和直觀。

等待多個(gè)通道的邏輯

在 Go 語(yǔ)言的 select 語(yǔ)句中,如果有多個(gè)通道操作同時(shí)準(zhǔn)備就緒(即都可以進(jìn)行),Go 運(yùn)行時(shí)會(huì)從這些通道操作中隨機(jī)選擇一個(gè)執(zhí)行。一旦某個(gè)通道操作被選中并執(zhí)行,其它通道的等待操作將不會(huì)繼續(xù)進(jìn)行。每次執(zhí)行 select 語(yǔ)句時(shí)都會(huì)重新評(píng)估所有通道操作。

示例:多個(gè)通道同時(shí)就緒

為了更好地理解這個(gè)機(jī)制,以下是一個(gè)示例,展示當(dāng)多個(gè)通道同時(shí)準(zhǔn)備就緒時(shí),select 語(yǔ)句的行為:

package main
import (
    "fmt"
    "time"
)
func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    ch3 := make(chan string)
    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "message from ch1"
    }()
    go func() {
        time.Sleep(1 * time.Second)
        ch2 <- "message from ch2"
    }()
    go func() {
        time.Sleep(1 * time.Second)
        ch3 <- "message from ch3"
    }()
    for i := 0; i < 3; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println(msg1)
        case msg2 := <-ch2:
            fmt.Println(msg2)
        case msg3 := <-ch3:
            fmt.Println(msg3)
        }
    }
}

在這個(gè)示例中,有三個(gè)通道 ch1, ch2, 和 ch3,每個(gè)通道在 1 秒后發(fā)送一個(gè)消息。因?yàn)樗型ǖ涝谕粫r(shí)間準(zhǔn)備就緒,select 語(yǔ)句將從中隨機(jī)選擇一個(gè)進(jìn)行處理,并打印相應(yīng)的消息。每次循環(huán)都會(huì)重新評(píng)估所有通道。

結(jié)論

當(dāng) select 語(yǔ)句等待多個(gè)通道時(shí),如果其中一個(gè)通道操作可以進(jìn)行,其它通道的操作不會(huì)繼續(xù)等待,而是等待下一次 select 語(yǔ)句的評(píng)估。每次 select 語(yǔ)句執(zhí)行時(shí)都會(huì)重新評(píng)估所有通道操作,并選擇其中一個(gè)可以進(jìn)行的操作。如果多個(gè)通道同時(shí)就緒,select 會(huì)隨機(jī)選擇其中一個(gè)進(jìn)行處理。

責(zé)任編輯:武曉燕 來(lái)源: 海燕技術(shù)棧
相關(guān)推薦

2018-12-04 14:00:41

協(xié)程編程模式PHP

2023-07-27 13:46:10

go開(kāi)源項(xiàng)目

2016-10-28 17:39:47

phpgolangcoroutine

2024-12-03 15:15:22

2021-05-20 09:14:09

Kotlin協(xié)程掛起和恢復(fù)

2021-04-25 09:36:20

Go協(xié)程線程

2024-06-27 07:56:49

2023-07-13 08:06:05

應(yīng)用協(xié)程阻塞

2025-02-28 09:04:08

2021-08-04 16:19:55

AndroidKotin協(xié)程Coroutines

2021-09-16 09:59:13

PythonJavaScript代碼

2021-05-21 08:21:57

Go語(yǔ)言基礎(chǔ)技術(shù)

2022-10-28 10:45:22

Go協(xié)程GoFrame

2024-08-27 09:46:39

Go協(xié)程效率

2023-12-27 08:07:49

Golang協(xié)程池Ants

2021-06-03 14:08:03

開(kāi)發(fā)技能代碼

2021-06-04 14:28:07

協(xié)程線程Android開(kāi)發(fā)

2023-11-17 11:36:59

協(xié)程纖程操作系統(tǒng)

2021-09-27 23:28:29

Go多協(xié)程并發(fā)

2025-02-08 09:13:40

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)