原文: http://blog.csdn.net/netdxy/article/details/54564436

在用 chan 类型时,发生死锁的错误,表面上看不出什么问题

-------------------------------------------------------------------------------------------------------

首先我们来看线程,在golang里面也叫goroutine

在读这篇文章之前,我们需要了解一下并发与并行。golang的线程是一种并发机制,而不是并行。它们之间的区别大家可以上网搜一下,网上有很多的介绍。

下面我们先来看一个例子吧

import("fmt"
)funcmain(){go fmt.Println("1")fmt.Println("2")
}

 在golang里面,使用Go这个关键字,后面再跟上一个函数就可以创建一个线程。后面的这个函数可以是已经写好的函数,也可以是一个匿名函数

funcmain(){var i=3go func(a int) {fmt.Println(a)fmt.Println("1")}(i)fmt.Println("2")}

上面的代码就创建了一个匿名函数,并且还传入了一个参数i,下面括号里的i是实参,a是形参。

那么上面的代码能按照我们预想的打印1、2、3吗?告诉你们吧,不能,程序只能打印出2。下面我把正确的代码贴出来吧

import("fmt""time"
)
funcmain(){var i = 3go func(a int) {fmt.Println(a)fmt.Println("1")}(i)fmt.Println("2")time.Sleep(1 * time.Second)
}

我只是在最后加了一行让主线程休眠一秒的代码,程序就会依次打印出2、3、1。

那为什么会这样呢?因为程序会优先执行主线程,主线程执行完成后,程序会立即退出,没有多余的时间去执行子线程。如果在程序的最后让主线程休眠1秒钟,那程序就会有足够的时间去执行子线程。

线程先讲到这里,下面我们来看看通道吧。

通道又叫channel,顾名思义,channel的作用就是在多线程之间传递数据的。

创建无缓冲channel

chreadandwrite :=make(chan int)

chonlyread := make(<-chan int) //创建只读channel 
chonlywrite := make(chan<- int) //创建只写channel 
下面我们来看一个例子:

ch :=make(chan int)     ch <- 1go func() {<-chfmt.Println("1")}()fmt.Println("2")

这段代码执行时会出现一个错误:fatal error: all goroutines are asleep - deadlock!

这个错误的意思是说线程陷入了死锁,程序无法继续往下执行。那么造成这种错误的原因是什么呢?

我们创建了一个无缓冲的channel,然后给这个channel赋值了,程序就是在赋值完成后陷入了死锁。因为我们的channel是无缓冲的,即同步的,赋值完成后来不及读取channel,程序就已经阻塞了。这里介绍一个非常重要的概念:channel的机制是先进先出,如果你给channel赋值了,那么必须要读取它的值,不然就会造成阻塞,当然这个只对无缓冲的channel有效。对于有缓冲的channel,发送方会一直阻塞直到数据被拷贝到缓冲区;如果缓冲区已满,则发送方只能在接收方取走数据后才能从阻塞状态恢复。

对于上面的例子有两种解决方案:

1、给channel增加缓冲区,然后在程序的最后让主线程休眠一秒,代码如下:

ch :=make(chan int,1)ch <- 1go func() {v := <-chfmt.Println(v)}()time.Sleep(1 * time.Second)fmt.Println("2")

 这样的话程序就会依次打印出1、2

2、把ch<-1这一行代码放到子线程代码的后面,代码如下:

ch :=make(chan int)go func() {v := <-chfmt.Println(v)}()ch <- 1fmt.Println("2")

  

这里就不用让主线程休眠了,因为channel在主线程中被赋值后,主线程就会阻塞,直到channel的值在子线程中被取出。

最后我们看一个生产者和消费者的例子:

import ("fmt""time"
)
func produce(p chan<- int) {for i := 0; i < 10; i++ {p <- ifmt.Println("send:", i)}
}
func consumer(c <-chan int) {for i := 0; i < 10; i++ {v := <-cfmt.Println("receive:", v)}
}
func main() {ch := make(chan int)go produce(ch)go consumer(ch)time.Sleep(1 * time.Second)
}

  

在这段代码中,因为channel是没有缓冲的,所以当生产者给channel赋值后,生产者这个线程会阻塞,直到消费者线程将channel中的数据取出。消费者第一次将数据取出后,进行下一次循环时,消费者的线程也会阻塞,因为生产者还没有将数据存入,这时程序会去执行生产者的线程。程序就这样在消费者和生产者两个线程间不断切换,直到循环结束。

下面我们再看一个带缓冲的例子:

import ("fmt""time"
)
func produce(p chan<- int) {for i := 0; i < 10; i++ {p <- ifmt.Println("send:", i)}
}
func consumer(c <-chan int) {for i := 0; i < 10; i++ {v := <-cfmt.Println("receive:", v)}
}
func main() {ch := make(chan int, 10)go produce(ch)go consumer(ch)time.Sleep(1 * time.Second)
}

在这个程序中,缓冲区可以存储10个int类型的整数,在执行生产者线程的时候,线程就不会阻塞,一次性将10个整数存入channel,在读取的时候,也是一次性读取。

转载于:https://www.cnblogs.com/oxspirt/p/7090625.html

【转】Golang 关于通道 Chan 详解相关推荐

  1. golang程序启动流程详解

    golang程序启动流程详解 环境 go1.16.5 linux/amd64 用例 package mainimport "fmt"func main() {fmt.Println ...

  2. Java NIO学习篇之通道FileChannel详解

    定义: FileChannel是Java NIO对应于磁盘等存储设备文件操作的通道. 常用API详解: 获取FileChannel的API /** * 打开一个与文件的连接通道,用于进行文件操作. * ...

  3. mysql ssh通道_详解如何通过SSH通道来访问MySQL

    原标题:详解如何通过SSH通道来访问MySQL 许多时候当要使用Mysql时,会遇到如下情况: 1. 信息比较重要,希望通信被加密. 2. 一些端口,比如3306端口,被路由器禁用. 对第一个问题的一 ...

  4. Golang map源码详解

    Golang的map是用哈希表实现的,在实现性能上非常优秀,这里会主要对map创建.插入.查询.删除以及删除全部的源码做详解,刻意避开了扩容以及迭代相关的代码,后续会用一个新的文章去讲述.Golang ...

  5. Golang基础知识入门详解

    Go语言入门 Go语言入门教程 很多人将 Go 语言 称为 21 世纪的 C 语言,因为 Go 不仅拥有 C 语言的简洁和性能,而且还很好的提供了 21 世纪互联网环境下服务端开发的各种实用特性,让开 ...

  6. golang包time用法详解

    在我们编程过程中,经常会用到与时间相关的各种务需求,下面来介绍 golang 中有关时间的一些基本用法,我们从 time 的几种 type 来开始介绍. 时间可分为时间点与时间段,golang 也不例 ...

  7. golang中的select详解

    注意 监听的case中,没有满足条件的就阻塞 多个满足条件的就任选一个执行 select本身不带循环,需要外层的for default通常不用,会产生忙轮询 break只能跳出select中的一个ca ...

  8. Golang垃圾回收机制详解

    Golang 从第一个版本以来,GC 一直是大家诟病最多的.但是每一个版本的发布基本都伴随着 GC 的改进.下面列出一些比较重要的改动. v1.1 STW v1.3 Mark STW, Sweep 并 ...

  9. golang——GMP调度模型详解

    目录 一.Golang调度器由来 存在问题: 3种协程和线程的关系 二.Golang对协程的处理 协程和goroutine关系 Go的GMP调度模型 P 和 M 何时会被创建 P和M的个数 调度器的设 ...

最新文章

  1. SQL SERVER 2008 数据恢复错误的解决步骤
  2. 【Linux】 命令收集
  3. fpga map测试_一种基于SELECTMAP的可配置且高速的FPGA配置电路及实现方法与流程
  4. iPhone13真香了?苹果官网被抢崩了,连夜补货!粉色或成爆款..
  5. java 中的static关键字和final关键字
  6. python多用户登录_python 多用户登录
  7. idea设置提示重复代码下去掉波浪线
  8. UWB与蓝牙AOA定位技术简要对比
  9. python图书管理实训报告总结_图书管理系统实训报告正文
  10. 用matlab计算矩阵的权重,如何使用matlab计算加权平均分
  11. 如何轻松搞定内网摄像头远程运维?EasyNTS上云网关简单三步实现设备公网远程控制、远程配置
  12. 狭义相对论推导过程中的数学悖论--有没有人能解释下?
  13. js中this指向学习总结
  14. 利用python下载视频
  15. 计算机程序设计员国家职业资格三级(高级)操作技能考核试卷-四川考场
  16. 计算机专业调剂化学专业,汕头大学计算机化学专业2015年考研调剂信息
  17. node生成图形验证码
  18. 前端flyUI的方法使用注意事项
  19. 云服务器测速脚本_Linux VPS服务器带宽测速脚本:Speedtest Linux进行网络测速方法...
  20. 汉明窗口Hamming Window

热门文章

  1. oracle数据库中could not get next sequence value的解决
  2. 吴恩达深度学习 —— 2.14 向量化逻辑回归的梯度输出
  3. Excel2010--在指定的范围内添加随机数
  4. Unity 4 3 制作一个2D横版射击游戏
  5. 各种排序算法比较--2015年7月23日22:33:43v1.0版
  6. 什么样的产品可以成功?
  7. 浏览器测试基本跑分网站
  8. c语言实验五函数答案,C语言程序设计实验五 参考答案.doc
  9. java 控制 sortedset_Java集合的checkedSortedSet()方法和示例
  10. 使用java连接ftp服务器_Java如何连接到FTP服务器?