[Golang] goroutine & channel

Channel

Channel 是 Go 語言中用於 goroutine 之間通信的管道,是 Go 並發編程模型的核心組件之一。
基本概念
Channel 提供了一種 goroutine 之間的通信機制,使一個 goroutine 可以向另一個 goroutine 發送數據,而無需顯式的鎖或條件變量。

類型與創建

// 創建不同類型的 channel
ch1 := make(chan int)         // 無緩衝的整數 channel
ch2 := make(chan string, 10)  // 有 10 個緩衝區的字符串 channel
ch3 := make(chan interface{}) // 任意類型的 channel

發送與接收

// 發送數據
ch <- value

// 接收數據
value := <-ch
value, ok := <-ch  // ok 表示 channel 是否已關閉

goroutine + Channel 範例:

package main
import "fmt"
import "time"
func main() {
    ch1 := make(chan int, 1)
    ch2 := make(chan string, 1)
    ch3 := make(chan interface{}, 1)

    ch1 <- 1  // ch1 緩衝區已滿

    // 創建 goroutine 來接收第一個值
    go func() {
        time.Sleep(2 * time.Second)  // 等待1秒
        fmt.Println(<-ch1)  // 1,此時釋放 ch1 的空間
    }()

    ch1 <- 3  // 這裡會阻塞,直到上面的 goroutine 從 ch1 接收數據
    // 由於上面的 goroutine 有2秒延遲,這里會等待約2秒

    ch2 <- "name"
    ch3 <- "hi"

    fmt.Println(<-ch1)  // 3
    fmt.Println(<-ch2)  // "name"
    fmt.Println(<-ch3)  // "hi"
}

顯示如下:

1
3
name
hi
  1. 範例&說明
package main
import "fmt"

func main() {
    // 創建通道
    c := make(chan int)

    //併發執行
    go ifunc(c)
    for i := 1; i <= 10; i++{
        // 將數據i發送給通道
        c <- i
    }

    // 通道ifunc結束讀取數據
    c<- 100

    //等於ifunc結束
    fmt.Println(<-c)
}

func ifunc(c chan int) {

    // 阻塞等待數據
    for {
        //從通道獲取一個數據
        data := <- c

        // 當取得的數據為100則結束阻塞
        if data == 100{
            break
        }
        // 輸出數據
        fmt.Println(data)
    }
    // 通知main結束
    c<-0
}

output:

1
2
3
4
5
6
7
8
9
10
0

補充:
可以在
fmt.Println(<-c)與fmt.Println(data)之前,print隨便一個數值,
就可以看到,輸出模式取決於 Go 調度器如何在這兩個 goroutine 之間切換

select

select 是 Go 語言中的一個控制結構,專門用於處理多個通道操作。它的主要用途是同時等待多個通道的操作(發送或接收),並在其中一個操作可以進行時執行對應的代碼。

基本語法

select {
case <-chan1:
    // 從 chan1 接收到數據時執行
case chan2 <- value:
    // 向 chan2 發送數據成功時執行
case x := <-chan3:
    // 從 chan3 接收到數據並賦值給 x 時執行
default:
    // 沒有通道操作就緒時執行(非阻塞模式)
}

select

package main

import (
    "fmt"
    "time"
)

func main() {
    // 創建兩個通道
    ch1 := make(chan string)
    ch2 := make(chan string)

    // 在一個 goroutine 中,1秒後發送數據到 ch1
    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "來自通道1的數據"
    }()
    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "來自通道1-1的數據"
    }()
    // 在另一個 goroutine 中,2秒後發送數據到 ch2
    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "來自通道2的數據"
    }()

    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "來自通道2-1的數據"
    }()

    // 使用 select 監聽兩個通道
    for i := 0; i < 4; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println("接收到:", msg1)
        case msg2 := <-ch2:
            fmt.Println("接收到:", msg2)
        case <-time.After(3 * time.Second):
            fmt.Println("操作超時")
        }
    }
}

output:

接收到: 來自通道1-1的數據
接收到: 來自通道1的數據
接收到: 來自通道2的數據
接收到: 來自通道2-1的數據

發佈留言

內容索引