参考文档:

https://www.liwenzhou.com/posts/Go/14_concurrence/

http://www.5lmh.com/并发编程/channel.html

示例一:

package main

import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func main() {
wg.Add(2)
go nobufChannel() //不带缓冲区的初始化
go bufChannel() //有缓冲区的通道
wg.Wait()
} //不带缓冲区的初始化
func nobufChannel() {
defer wg.Done()
channel1 := make(chan int)
go func() {
x := <-channel1
fmt.Println("channel里取出的数字:", x)
close(channel1)
}()
channel1 <- 10
fmt.Println("10存入channel") } //有缓冲区的通道
func bufChannel() {
defer wg.Done()
channel2 := make(chan int, 1) //指定了只能放一个数
channel2 <- 10
fmt.Println("10 发送到通道用bufChannel")
//channel2 <- 20 //todo 指定了只能放一个数,取出后,才能继续向通道里存入数据
//fmt.Println("20 发送到通道用bufChannel")
ch2 := <-channel2
fmt.Println("取出bufChannel中的数据,", ch2)
}

示例二:

package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
var once sync.Once
func main() {
fmt.Println("我是返回值")
a := make(chan int, 100)
b := make(chan int, 100)
wg.Add(3)
go setValue(a)
go getValue(a, b)
go getValue(a, b)
for ret :=range b{
fmt.Println(ret) //循环打印b通道中的数据
}
wg.Wait()
}
func setValue(a chan int) {
defer wg.Done()
//循环的存入通道中
for i := 1; i <= 100; i++ {
a <- i
}
close(a)
} func getValue(a, b chan int) {
defer wg.Done()
for {
v,ok:= <-a
if !ok {
break
}
b <- v //循环读出a通道中的数据,再的存入b通道中
}
once.Do(func(){close(b)})
}

close()

可以通过内置的close()函数关闭channel(如果你的管道不往里存值或者取值的时候一定记得关闭管道)

package main

import "fmt"

/*
1.对一个关闭的通道再发送值就会导致panic。
2.对一个关闭的通道进行接收会一直获取值直到通道为空。
3.对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。
4.关闭一个已经关闭的通道会导致panic。 */ func main() {
a := make(chan int, 2)
a <- 10
a <- 20
close(a)
//for k := range a {
// fmt.Println(k)
//} v, ok := <-a
fmt.Println(v, ok) //10 true
v, ok = <-a
fmt.Println(v, ok) //20 true
v, ok = <-a
fmt.Println(v, ok) //0 false
v, ok = <-a
fmt.Println(v, ok) //0 false
}

如何优雅的从通道循环取值

当通过通道发送有限的数据时,我们可以通过close函数关闭通道来告知从该通道接收值的goroutine停止等待。当通道被关闭时,往该通道发送值会引发panic,从该通道里接收的值一直都是类型零值。那如何判断一个通道是否被关闭了呢?

我们来看下面这个例子:

package main

import "fmt"

// channel 练习
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
// 开启goroutine将0~100的数发送到ch1中
go func() {
for i := 0; i < 100; i++ {
ch1 <- i
}
close(ch1)
}()
// 开启goroutine从ch1中接收值,并将该值的平方发送到ch2中
go func() {
for {
i, ok := <-ch1 // 通道关闭后再取值ok=false
if !ok {
break
}
ch2 <- i * i
}
close(ch2)
}()
// 在主goroutine中从ch2中接收值打印
for i := range ch2 { // 通道关闭后会退出for range循环
fmt.Println(i)
}
}

从上面的例子中我们看到有两种方式在接收值的时候判断通道是否被关闭,我们通常使用的是for range的方式。

实现斐波那契数列

//实现斐波那契数列:1 1 2 3 5 8
package main import "fmt" //此时的ch只写,quit只读
func fibonacci(ch chan<- int, quit <-chan bool) {
x, y := 1, 1
for {
//监听channel数据的流动
select {
case ch <- x:
x, y = y, x+y
case flag := <-quit: //没数据时一直处于阻塞状态
fmt.Println("flag=", flag)
return //不能用break,因为只跳出select这一层
}
}
}
func main() {
//创建双向通道,没有缓冲的
ch := make(chan int) //数字通信
quit := make(chan bool) //程序是否结束 //生产者,从channel读取内容
//新建协程
go func() {
for i := 0; i < 8; i++ {
num := <-ch //只读,没数据时,处于阻塞状态
fmt.Println("num=", num)
}
//可以停止
quit <- true //写
}() //生产者,产生数字,写入channel
fibonacci(ch, quit)
}

通道总结

channel常见的异常总结,如下图:

注意:关闭已经关闭的channel也会引发panic。

Go语言基础之并发 goroutine chan的更多相关文章

  1. GO学习-(18) Go语言基础之并发

    Go语言基础之并发 并发是编程里面一个非常重要的概念,Go语言在语言层面天生支持并发,这也是Go语言流行的一个很重要的原因. Go语言中的并发编程 并发与并行 并发:同一时间段内执行多个任务(你在用微 ...

  2. Go语言基础之并发

    并发是编程里面一个非常重要的概念,Go语言在语言层面天生支持并发,这也是Go语言流行的一个很重要的原因. Go语言中的并发编程 并发与并行 并发:同一时间段内执行多个任务(你在用微信和两个女朋友聊天) ...

  3. GO_11:GO语言基础之并发concurrency

    并发Concurrency 很多人都是冲着 Go 大肆宣扬的高并发而忍不住跃跃欲试,但其实从源码的解析来看,goroutine 只是由官方实现的超级“线程池”而已.不过话说回来,每个实例 4-5KB的 ...

  4. GO语言基础之并发concurrency

    并发Concurrency 很多人都是冲着 Go 大肆宣扬的高并发而忍不住跃跃欲试,但其实从源码的解析来看,goroutine 只是由官方实现的超级“线程池”而已.不过话说回来,每个实例 4-5KB的 ...

  5. Golang语言系列-11-goroutine并发

    goroutine 并发 概念 package main import ( "fmt" "time" ) /* [Go语言中的并发编程 goroutine] [ ...

  6. Go语言中的并发编程

    并发是编程里面一个非常重要的概念,Go语言在语言层面天生支持并发,这也是Go语言流行的一个很重要的原因. Go语言中的并发编程 并发与并行 并发:同一时间段内执行多个任务(你在用微信和两个女朋友聊天) ...

  7. Go语言系列之并发编程

    Go语言中的并发编程 并发与并行 并发:同一时间段内执行多个任务(宏观上并行,微观上并发). 并行:同一时刻执行多个任务(宏观和微观都是并行). Go语言的并发通过goroutine实现.gorout ...

  8. Go语言基础之函数

    Go语言基础之函数 函数是组织好的.可重复使用的.用于执行指定任务的代码块.本文介绍了Go语言中函数的相关内容. 函数 Go语言中支持函数.匿名函数和闭包,并且函数在Go语言中属于“一等公民”. 函数 ...

  9. Go语言基础之1--标识符、关键字、变量和常量、数据类型、Go的基本程序结构、Golang的特性

    一.前言 当我们项目较为简单时,我们在src目录下新建一个该项目目录,里面存放源码文件即可,见下图: 当我们一个项目较为复杂时,我们可以在src目录下新建一个该项目目录,在针对该项目不同模块创建不同目 ...

  10. Go 语言基础

    Go 语言基础 特点 常用命令 go run 直接运行, 不会生成可执行文件 go build 生成可执行文件, 推荐 加分特点 UTF-8编码 高并发: go 关键字 管道: pipe := mak ...

随机推荐

  1. .editorConfig常用设置

    # http://editorconfig.org root = true [*] # 表示所有文件适用charset = utf-8 # 设置文件字符集为 utf-8indent_style = s ...

  2. redis的两种持久化方式

    1.为什么redis需要持久化 答:edis是基于内存的,如果Redis服务器挂了,数据就会丢失 2.有几种方式实现redis的持久化 答:有两种,一种是AOF 持久化,另一种是RDB持久化 一. A ...

  3. #背包#nssl 1488 上升子序列

    题目 给一个长度为\(n\)的数组\(a\).试将其划分为两个严格上升子序列,并使其长度差最小. 分析 当\(max([1,i])<min([i+1,n])\)时显然两个区间互不影响,把\(i\ ...

  4. 1开幕在即 | “万物互联,使能千行百业”2022开放原子全球开源峰会OpenAtom OpenHarmony分论坛

    7月27日下午,聚焦开源产业与生态的2022开放原子全球开源峰会OpenAtom OpenHarmony分论坛将在北京亦创国际会展中心盛大开幕. 作为OpenHarmony工作委员会联合生态合作伙伴为 ...

  5. SQL 数据库语句- 创建和管理数据库

    SQL CREATE DATABASE 语句 SQL CREATE DATABASE 语句用于创建一个新的 SQL 数据库. 语法 CREATE DATABASE 数据库名称; 示例 以下 SQL 语 ...

  6. 手撸jdk源码分析类加载机制

    我们一般写的java文件jvm是识别不了的,因此需要编译,编译后会变成.class文件,而要执行代码,jvm首先会去加载.class文件到内存中,那么他的流程是什么样的呢: 1.首先肯定创建java虚 ...

  7. Python制作词云--stylecloud简单使用

    安装 pip install stylecloud 使用 from stylecloud import gen_stylecloud gen_stylecloud('zhangsan lisi wan ...

  8. HDC 2022精彩继续,多重亮点进来看!

    原文:https://mp.weixin.qq.com/s/YX5vD4cxM8dA4v2ukFooyA,点击链接查看更多技术内容.  

  9. Spring6

    0x00 环境配置 环境: IDEA >= 2022.1.4 JDK 17 Maven 3.8.6 Spring 6.0.0 JUnit 4.13.2 Log4j2 新建模块 spring001 ...

  10. HTC vive开发:关于手柄按键对接控制

    一.关于左右手柄的对应关系 两个手柄和SteamVR_TrackedObject.EIndex是对应的,一个是EIndex.Device2,另一个是EIndex.Device3(有编号的那个) 在场景 ...