Go channel
channel 传递数据都是左箭头?
在 Go 语言中,通道(channel)用于在不同的 goroutine 之间传递数据。通道操作使用箭头符号 <-,但箭头的方向取决于数据的传递方向。以下是详细说明:
概括
通道的操作中,箭头符号 <- 表示数据的传递方向:
ch <- value:将数据发送到通道。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)
}
在上面的例子中,ch1 和 ch2 都有可能在相同时间点可读,因此 select 会随机选择一个进行执行。
详细解释
- 多路复用:
select允许在多个通道操作中进行选择,这对于处理多个通道数据或事件非常有用。 - 随机性:当多个
case都可以执行时,select会随机选择一个进行执行。这是为了避免程序在特定条件下总是选择第一个可执行的case,从而提高并发程序的公平性。 - 避免死锁:
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")
}
}
在这个示例中,ch1 和 ch2 都会在 1 秒后变为可读,select 会随机选择一个进行执行。
总结
select 语句在 Go 语言中用于在多个通道操作中进行选择,当有多个 case 都可以执行时,它会随机选择一个进行执行。这种机制有助于避免偏向性,提高并发程序的公平性。
Golang select 的使用及典型用法

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 之间传递数据和进行同步。它们简化了并发编程,避免了显式的锁和条件变量。通过通道,你可以实现数据传递、同步、任务分发和限制并发量等功能,从而编写出高效、安全的并发程序。