第七章:并发编程 2.Channels --Go 语言轻松入门

Go语言中的通道(Channel)是一种特殊的类型,用于在不同的goroutine之间传递数据和同步执行。通道提供了一种安全的方式来避免数据竞争,并且简化了并发编程的复杂性。下面是关于Go Channels的一些关键点:

1. 基本概念

  • 定义var ch chan int 定义了一个整数类型的通道。
  • 创建 :使用 make 函数创建通道,例如 ch := make(chan int) 或者 ch := make(chan int, 10) 创建一个带缓冲的通道。
  • 方向性 :可以定义单向通道,如 chan<- int 只能发送,<-chan int 只能接收。
go 复制代码
package main

import (
	"fmt"
	"time"
)

// 定义一个只发送整数的函数
func sendNumbers(ch chan<- int) {
	for i := 0; i < 5; i++ {
		fmt.Printf("Sending: %d\n", i)
		ch <- i
		time.Sleep(1 * time.Second)
	}
	close(ch) // 发送完成后关闭通道
}

// 定义一个只接收整数并处理的函数
func processNumbers(ch <-chan int) {
	for v := range ch {
		fmt.Printf("Processing: %d\n", v)
		time.Sleep(1 * time.Second)
	}
}

func main() {
	// 创建一个无缓冲的整型通道
	ch := make(chan int)

	// 启动发送者goroutine
	go sendNumbers(ch)

	// 启动处理者goroutine
	go processNumbers(ch)

	// 等待一段时间让所有goroutines完成
	time.Sleep(10 * time.Second)
	fmt.Println("All messages processed, exiting.")
}

2. 操作

  • 发送ch <- value 将值发送到通道中。
  • 接收value := <- ch 从通道中接收值。
  • 多值接收value, ok := <- ch 接收值的同时检查通道是否关闭 (okfalse 表示通道已关闭)。

3. 阻塞与非阻塞

  • 无缓冲通道:发送和接收操作会一直阻塞,直到另一方准备好。
go 复制代码
package main

import (
	"fmt"
)

func main() {
	// 创建一个无缓冲的整型通道
	ch := make(chan int)

	// 启动一个发送者goroutine
	go func() {
		for i := 0; i < 5; i++ {
			fmt.Printf("Sender: Sending %d\n", i)
			ch <- i // 将i发送到通道ch
			// time.Sleep(1 * time.Second) // 模拟耗时操作
		}
		close(ch) // 发送完成后关闭通道
	}()

	// 主goroutine作为接收者
	for v := range ch {
		fmt.Printf("Receiver: Received %d\n", v)
	}

	fmt.Println("All messages received, exiting.")
}
  • 有缓冲通道:只有当缓冲区满时发送才会阻塞;只有当缓冲区为空时接收才会阻塞。
go 复制代码
package main

import (
	"fmt"
)

func main() {
	// 创建一个带有缓冲区大小为2的整型通道
	ch := make(chan int, 2)

	// 启动一个发送者goroutine
	go func() {
		for i := 0; i < 10; i++ {
			fmt.Printf("Sender: Sending %d\n", i)
			ch <- i // 将i发送到通道ch
			// time.Sleep(1 * time.Second) // 模拟耗时操作
		}
		close(ch) // 发送完成后关闭通道
	}()

	// 主goroutine作为接收者
	for v := range ch {
		fmt.Printf("Receiver: Received %d\n", v)
	}

	fmt.Println("All messages received, exiting.")
}

4. 关闭通道

  • 使用 close(ch) 关闭通道。关闭后的通道不能再发送数据,但可以继续接收剩余的数据直到通道为空。

5. 选择器

  • select 语句允许在多个通信操作中进行选择,类似于 switch 语句,但是只处理通信操作。
  • default 子句可以让 select 在没有可用的case时执行。
go 复制代码
package main

import (
	"fmt"
	"time"
)

// main 是程序的入口点。
func main() {
	// 创建一个无缓冲的channel,用于接收异步操作的结果。
	ch := make(chan int)

	// 启动一个goroutine,在后台执行操作。
	go func() {
		// 模拟一些耗时操作,比如等待I/O完成。
		time.Sleep(3 * time.Second)
		// 将结果发送到channel。
		ch <- 42
	}()

	// 2秒后超时,生成一个超时信号。
	timeout := time.After(2 * time.Second)

	// 无限循环,等待结果或超时。
	for {
		select {
		// 当channel接收到值时。
		case value := <-ch:
			// 打印接收到的值并退出程序。
			fmt.Println("Received:", value)
			return
		// 当超时信号触发时。
		case <-timeout:
			// 打印超时信息并退出程序。
			fmt.Println("Timed out")
			return
		// 默认情况,即没有接收到值也没有超时。
		default:
			// 打印等待信息,并短暂暂停以避免忙等待。
			fmt.Println("Still waiting...")
			time.Sleep(500 * time.Millisecond)
		}
	}
}

6. 范围循环

  • for range 循环可以用来遍历通道中的数据,直到通道被关闭并且所有数据都被读取。

7. 应用场景

  • 并行计算:通过通道收集结果。
  • 事件分发:作为事件处理器之间的消息队列。
  • 资源池:管理一组可重用资源。

8. 注意事项

  • 不要对同一个通道同时进行多次接收或发送操作。
go 复制代码
package main

import (
	"fmt"
)

// main 是程序的入口点
func main() {
	// 创建一个带有缓冲区的整型通道
	ch := make(chan int, 5) // 缓冲区大小为5

	// 启动两个发送者goroutine
	go func() {
		for i := 0; i < 5; i++ {
			fmt.Printf("Sender 1: Sending %d\n", i)
			ch <- i
		}
	}()

	go func() {
		for i := 10; i < 15; i++ {
			fmt.Printf("Sender 2: Sending %d\n", i)
			ch <- i
		}
	}()

	// 接收者goroutine
	for i := 0; i < 10; i++ {
		fmt.Printf("Receiver: Received %d\n", <-ch)
	}
}
  • 当通道不再使用时应该关闭它,以便接收方能够检测到通道关闭。
  • 使用 range 迭代通道时确保最终会关闭通道,否则会导致死锁。

通过理解和运用这些概念,你可以利用Go的通道来构建高效、可靠的并发程序。

相关推荐
苏打水com4 分钟前
数据库进阶实战:从性能优化到分布式架构的核心突破
数据库·后端
csbysj202035 分钟前
如何使用 XML Schema
开发语言
R6bandito_40 分钟前
STM32中printf的重定向详解
开发语言·经验分享·stm32·单片机·嵌入式硬件·mcu
earthzhang20211 小时前
【1007】计算(a+b)×c的值
c语言·开发语言·数据结构·算法·青少年编程
杨枝甘露小码1 小时前
Python学习之基础篇
开发语言·python
间彧1 小时前
Spring Cloud Gateway与Kong或Nginx等API网关相比有哪些优劣势?
后端
间彧1 小时前
如何基于Spring Cloud Gateway实现灰度发布的具体配置示例?
后端
间彧1 小时前
在实际项目中如何设计一个高可用的Spring Cloud Gateway集群?
后端
间彧1 小时前
如何为Spring Cloud Gateway配置具体的负载均衡策略?
后端
间彧1 小时前
Spring Cloud Gateway详解与应用实战
后端