channel一篇全搞定

什么是channel

channel是一个数据管道,可以往里面写数据,可以从里面读数据,channel是goroutine之间数据通信的桥梁,而且是线程安全的,channel遵循先进先出的原则,写入数据和读出数据都会加锁。

channel的分类

从读写的角度来区分

  • 只读channel,单向channel
  • 只写channel,单向channel
  • 可读可写channel

从是否带有缓冲区来区分

  • 带缓冲区的channel,定义了缓冲区的大小,可以存储多个数据
  • 不带缓冲区的channel,只能存一个数据,并且只有当该数据被取出的时候才能存下一个数据
channel定义和声明

channel 按照声明和定义的方式可以从读写的方式上划分为只读的channel、只写的channel、可读可写的channel; 从带不带缓存区的角度又可以划分为带缓存区的channel和不带缓存区的channel。

readOnlyChan := make(<- chan int)
writeOnlyChan := make(chan <- int)
readAndWriteChan := make(chan int)


readOnlyChan := make(<- chan int,1)
writeOnlyChan := make(chan <- int,1)
readAndWriteChan := make(chan int,1)
对channel的操作方式

针对channel的操作有下面的几种方式,从channel 中读取数据、将数据写入channel、关闭channel

  • 实践一
    ch := make(chan int)
    go func() {
        ch <- 10
    }()
    fmt.Println(<-ch)
  • 实践二
{
	ch := make(chan int)
	go store(ch)
	for i := 0; i < 10; i++ {
		fmt.Println("received ", <-ch)
	}
}

func store(ch chan int) {
	for i := 0; i < 10; i++ {
		fmt.Println("send ", i)
		ch <- i
	}
}
  • 实践三
// 带缓存的channel
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(<-ch)

实践四

{
	s := []int{1, 2, 4, 5, 9}
	ch := make(chan int)
	go sums(s[:len(s)/2], ch)
	go sums(s[len(s)/2:], ch)
	x, y := <-ch, <-ch
	fmt.Println(x, y, x+y)
}

func sums(s []int, c chan int) {
	sum := 0
	for _, v := range s {
		sum += v
	}
	c <- sum
}
判断channel是否关闭

判断一个channel是否关闭的语法如下:如果ok为true则能够读到数据,说明channel没有关闭;如果ok为false,说明不能够读到数据,channel已关闭。

v, ok := <- ch

实践一

{
	ch := make(chan int)
	go test(ch)

	for {
		if v, ok := <-ch; ok {
			fmt.Println("get value: ", v)
		} else {
			break
		}
	}
}

func test(ch chan int) {
	for i := 0; i < 5; i++ {
		ch <- i
	}
	close(ch)
}
channel超时处理

channel 可以结合time实现超时处理功能,当从channel中读取数据超过一定的时间的时候如果还没有数据到来,那么就可以认为当前的读取操作已经超时,防止goroutine一直处于阻塞状态。

// 读取ch中的数据,然后一直等待,直到超时后推出

{
    ch := make(chan int)
	quit := make(chan bool)
	go func() {
		for {
			select {
			case v := <-ch:
				fmt.Println(v)
			case <-time.After(time.Second * 3):
				quit <- true
				fmt.Println("timeout send notice")
				return
			}
		}
	}()

	for i := 0; i < 4; i++ {
		ch <- i
	}

	<-quit
	fmt.Println("main exit")
}
<think>我们正在讨论Go语言中channel和struct的使用。根据用户需求,我们需要提供关于channel和struct的教程或学习文章。结合之前的引用内容,我们可以知道channel的基本使用(声明、初始化、发送和接收),以及struct的定义和使用。由于用户同时提到了channel和struct,我们可以考虑如何将两者结合使用,例如在channel中传递struct类型的数据。回答结构:1.介绍channel和struct的基本概念。2.分别讲解struct的定义和channel的创建。3.展示如何在channel中使用struct(即通过channel传递struct类型的值)。4.给出一个综合示例,展示如何使用channel和struct进行并发编程。5.提供相关学习资源或进一步阅读建议。注意:引用内容中已经包含了channel的一些基本知识,我们可以适当引用。根据引用[1]和[2]:channel需要指定类型,可以使用make创建,有缓冲和无缓冲两种。struct是自定义类型,可以包含多个字段。我们按照以下步骤组织回答:第一步:struct的定义第二步:channel的创建(针对struct类型)第三步:在goroutine中使用channel传递struct第四步:示例代码同时,我们需要生成相关问题。</think>### Go语言中channel和struct的使用教程 #### 一、struct基础 struct是Go语言中的复合数据类型,用于封装多个字段: ```go type Person struct { Name string Age int Addr string } // 初始化方式 p1 := Person{"Alice", 30, "Beijing"} p2 := Person{Name: "Bob", Age: 25} ``` #### 二、channel基础 channel是Go的并发原语,用于goroutine间通信: 1. **创建channel** 引用[1]:使用`make()`创建,需指定元素类型: ```go // 无缓冲channel ch1 := make(chan Person) // 有缓冲channel(容量10) ch2 := make(chan Person, 10) ``` 2. **发送与接收数据** 引用[2]:通过`<-`操作符通信: ```go // 发送数据到channel ch1 <- Person{"Tom", 28, "Shanghai"} // 从channel接收数据 p := <-ch1 ``` #### 三、结合使用channel和struct ##### 典型场景:goroutine间传递结构化数据 ```go package main type Task struct { ID int Data string Status chan string // channel作为struct字段 } func worker(taskChan chan Task) { for task := range taskChan { // 处理任务 result := process(task.Data) // 通过struct内的channel返回状态 task.Status <- fmt.Sprintf("Task %d: %s", task.ID, result) } } func main() { taskChan := make(chan Task, 5) // 启动worker go worker(taskChan) // 创建带状态channel的Task statusCh := make(chan string) task := Task{ID: 1, Data: "file1", Status: statusCh} // 发送任务 taskChan <- task // 接收状态 fmt.Println(<-statusCh) // 输出: Task 1: processed_file1 } ``` ##### 关键应用场景: 1. **工作池模式** 引用[3]:使用buffered channel实现任务队列: ```go tasks := make(chan Task, 100) for i := 0; i < 10; i++ { go worker(tasks) // 10个worker并发处理 } ``` 2. **事件广播** 通过关闭channel通知所有接收者: ```go type Server struct { stopChan chan struct{} } func (s *Server) Stop() { close(s.stopChan) // 广播停止信号 } ``` 3. **互斥控制** 利用channel实现信号量: ```go type Resource struct { sem chan struct{} } func (r *Resource) Use() { r.sem <- struct{}{} // 获取令牌 defer func() { <-r.sem }() // 释放令牌 // 安操作... } ``` #### 四、注意事项 1. **channel零值** 引用[2]:未初始化的channel默认为`nil`,发送/接收会永久阻塞 2. **死锁预防** 无缓冲channel需配对发送/接收操作 3. **资源释放** 不再使用的channel应显式关闭避免内存泄漏 #### 学习资源推荐 1. [官方并发指南](https://golang.org/doc/effective_go#concurrency) 2. [Go by Example: Channels](https://gobyexample.com/channels) 3. [深入解析Channel实现原理](https://draveness.me/golang/concurrency/golang-channel.html)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值