Skip to main content

Go channel

channel 传递数据都是左箭头?

在 Go 语言中,通道(channel)用于在不同的 goroutine 之间传递数据。通道操作使用箭头符号 <-,但箭头的方向取决于数据的传递方向。以下是详细说明:

概括

通道的操作中,箭头符号 <- 表示数据的传递方向:

  1. ch <- value:将数据发送到通道。
  2. value := <-ch:从通道接收数据。

详细说明

发送数据到通道

当你想要将数据发送到通道时,箭头符号 <- 的方向是从数据到通道的,即 ch <- value

ch := make(chan int)
go func() {
ch <- 42 // 将数据 42 发送到通道 ch
}()

从通道接收数据

当你想要从通道接收数据时,箭头符号 <- 的方向是从通道到数据的,即 value := <-ch

value := <-ch // 从通道 ch 接收数据,并赋值给变量 value
fmt.Println(value) // 输出 42

示例代码

以下是一个完整的示例,展示了如何在不同的 goroutine 之间使用通道传递数据:

package main

import (
"fmt"
)

func main() {
ch := make(chan int)

go func() {
ch <- 42 // 将数据 42 发送到通道 ch
}()

value := <-ch // 从通道 ch 接收数据,并赋值给变量 value
fmt.Println(value) // 输出 42
}

单向通道

Go 还支持单向通道,即只能发送或只能接收数据的通道。单向通道的声明如下:

  • 只能发送数据的通道:chan<- int
  • 只能接收数据的通道:<-chan int
func sendData(ch chan<- int) {
ch <- 42 // 只能发送数据
}

func receiveData(ch <-chan int) {
value := <-ch // 只能接收数据
fmt.Println(value)
}

func main() {
ch := make(chan int)
go sendData(ch)
receiveData(ch)
}

总结

在 Go 语言中,通道操作使用箭头符号 <- 来表示数据的传递方向。发送数据到通道时,使用 ch <- value;从通道接收数据时,使用 value := <-ch。此外,Go 还支持单向通道,用于限制通道的操作方向。

理解:select 会随机执行一个可运行的 case

在 Go 语言中,select 语句用于选择多个通道操作中的一个进行执行。确实,如果有多个通道操作都可以执行,select 会随机选择一个进行执行,而不是按顺序或优先级选择。以下是一些详细说明:

概括

select 语句会随机选择一个可运行的 case 执行,而不是按顺序或优先级选择。

详细说明

基本用法

select 语句的基本结构如下:

select {
case <-ch1:
// 当 ch1 可读时执行
case ch2 <- value:
// 当 ch2 可写时执行
default:
// 当没有其他 case 可执行时执行
}

随机选择

当有多个 case 都可以执行时,select 会随机选择其中一个执行。这种随机选择有助于避免偏向性,从而使程序更加公平和并发友好。

例如:

ch1 := make(chan int)
ch2 := make(chan int)

go func() {
ch1 <- 1
}()

go func() {
ch2 <- 2
}()

select {
case val := <-ch1:
fmt.Println("Received from ch1:", val)
case val := <-ch2:
fmt.Println("Received from ch2:", val)
}

在上面的例子中,ch1ch2 都有可能在相同时间点可读,因此 select 会随机选择一个进行执行。

详细解释

  1. 多路复用select 允许在多个通道操作中进行选择,这对于处理多个通道数据或事件非常有用。
  2. 随机性:当多个 case 都可以执行时,select 会随机选择一个进行执行。这是为了避免程序在特定条件下总是选择第一个可执行的 case,从而提高并发程序的公平性。
  3. 避免死锁select 语句中的 default 子句可以防止 select 语句阻塞,从而避免死锁。

示例

以下是一个更复杂的示例,展示了 select 如何在多个通道操作中进行选择:

package main

import (
"fmt"
"time"
)

func main() {
ch1 := make(chan int, 1)
ch2 := make(chan int, 1)

go func() {
time.Sleep(1 * time.Second)
ch1 <- 1
}()

go func() {
time.Sleep(1 * time.Second)
ch2 <- 2
}()

select {
case val := <-ch1:
fmt.Println("Received from ch1:", val)
case val := <-ch2:
fmt.Println("Received from ch2:", val)
default:
fmt.Println("No channels are ready")
}
}

在这个示例中,ch1ch2 都会在 1 秒后变为可读,select 会随机选择一个进行执行。

总结

select 语句在 Go 语言中用于在多个通道操作中进行选择,当有多个 case 都可以执行时,它会随机选择一个进行执行。这种机制有助于避免偏向性,提高并发程序的公平性。

Golang select 的使用及典型用法

alt text

goroutine 之间进行通信和同步的机制

在 Go 语言中,通道(channel)是一种用于在不同的 goroutine 之间进行通信和同步的机制。通道允许你安全地在多个 goroutine 之间传递数据,而不需要使用复杂的锁机制。以下是通道的一些主要用途和详细说明:

概括

通道用于在 goroutine 之间传递数据和进行同步,以实现安全并发编程。

1. 数据传递

通道最主要的用途是在线程(goroutine)之间传递数据。通过通道,你可以将一个 goroutine 中的数据发送到另一个 goroutine 中。

package main

import (
"fmt"
)

func main() {
ch := make(chan int)

go func() {
ch <- 42 // 将数据 42 发送到通道 ch
}()

value := <-ch // 从通道 ch 接收数据,并赋值给变量 value
fmt.Println(value) // 输出 42
}

2. 同步

通道还可以用于同步多个 goroutine 的执行。例如,你可以使用通道来等待某个 goroutine 完成特定任务。

package main

import (
"fmt"
"time"
)

func main() {
done := make(chan bool)

go func() {
time.Sleep(2 * time.Second)
fmt.Println("Task completed")
done <- true // 发送完成信号
}()

<-done // 等待完成信号
fmt.Println("All tasks completed")
}

3. 任务分发

通道可以用于将任务分发到多个工作 goroutine 中,从而实现并发任务处理。

package main

import (
"fmt"
"sync"
)

func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
for j := range jobs {
fmt.Printf("Worker %d processing job %d\n", id, j)
results <- j * 2
}
}

func main() {
const numJobs = 5
jobs := make(chan int, numJobs)
results := make(chan int, numJobs)

var wg sync.WaitGroup
for w := 1; w <= 3; w++ {
wg.Add(1)
go worker(w, jobs, results, &wg)
}

for j := 1; j <= numJobs; j++ {
jobs <- j
}
close(jobs)

wg.Wait()
close(results)

for result := range results {
fmt.Println("Result:", result)
}
}

4. 限制并发量

通道可以用于限制同时运行的 goroutine 数量,从而控制并发量。

package main

import (
"fmt"
"time"
)

func worker(id int, semaphore chan struct{}) {
defer func() { <-semaphore }()
fmt.Printf("Worker %d starting\n", id)
time.Sleep(2 * time.Second)
fmt.Printf("Worker %d done\n", id)
}

func main() {
const maxConcurrency = 3
semaphore := make(chan struct{}, maxConcurrency)

for i := 1; i <= 10; i++ {
semaphore <- struct{}{}
go worker(i, semaphore)
}

// 等待所有 goroutine 完成
for i := 0; i < maxConcurrency; i++ {
semaphore <- struct{}{}
}
}

总结

通道在 Go 语言中是一个强大的工具,用于在 goroutine 之间传递数据和进行同步。它们简化了并发编程,避免了显式的锁和条件变量。通过通道,你可以实现数据传递、同步、任务分发和限制并发量等功能,从而编写出高效、安全的并发程序。