channel是Go语言中的一个核心数据类型,channel是一个数据类型,主要用来解决协程的同步问题以及协程之间数据共享(数据传递)的问题。在并发核心单元通过它就可以发送或者接收数据进行通讯,这在一定程度上又进一步降低了编程的难度。

goroutine运行在相同的内存地址空间,channel可以避开所有内存共享导致的坑;通道的通信方式保证了同步性。数据通过channel:同一时间只有一个协程可以访问数据:所以不会出现数据竞争,确保并发安全。

channel的定义

channel是对应make创建的底层数据结构的引用。 创建语法: make(chan Type, capacity)

channel := make(chan bool) //创建一个无缓冲的bool型Channel
,等价于make(chan Type, 0)
channel := make(chan bool, 1024) //创建一个有缓冲,切缓冲区为1024的bool型Channel
 channel <- x //向一个Channel发送一个值
<- channel //从一个Channel中接收一个值
x = <- channel //从Channel c接收一个值并将其存储到x中
x, ok = <- channel //从Channel接收一个值,如果channel关闭了或没有数据,那么ok将被置为false

channel是一个引用类型,当复制一个channel或用于函数参数传递时,我们只是拷贝了一个channel引用,因此调用者和被调用者将引用同一个channel对象。和其它的引用类型一样,channel的零值(定义未初始化)也是nil。

在默认情况下,channel接收发送数据都是阻塞的,(channel <- 1,写端写数据,读端不在读。写端阻塞; str := <-channel 读端读数据, 同时写端不在写,读端阻塞。)除非另一端已经准备好,这样就使得goroutine同步变的更加的简单,而不需要显式的lock。

示例

package main

import (
"fmt"
"runtime"
"time"
) var c = make(chan int32) func printstr(s string) {
for _, value := range s {
fmt.Printf("写入%+q\r\n", value)
time.Sleep(time.Second)
c <- value
}
} func main() {
runtime.GOMAXPROCS(1)
go func() {
time.Sleep(time.Second)
printstr("hello")
}() go func() {
for v := range c {
fmt.Printf("读取%+q\r\n", v)
}
}()
for {
;
}
}

channel的缓冲

无缓冲的channel

无缓冲的channel unbuffered channel 是指在接收前没有能力保存任何值的通道。这种类型的channel 要求发送端和接收端同时准备好,才能完成发送和接收操作。否则,通道会导致先执行发送或接收操作的阻塞等待。顾又称为同步通信

  • 阻塞:由于某种原因数据没有到达,当前协程(线程)持续处于等待状态,直到条件满足,才接触阻塞。
  • 同步:在两个或多个协程(线程)间,保持数据内容一致性的机制。

示例如上,写了没有读会导致阻塞,读了没有写会导致堵塞

有缓冲的channel

有缓冲的通道(buffered channel)是一种在被接收前能存储一个或者多个数据值的通道。这种类型的channel并不强制要求goroutine之间必须同时完成发送和接收。通道会阻塞发送和接收动作的条件也不同。

  • 只有channel通道中没有要接收的值时,接收动作才会阻塞。
  • 只有通道没有可用缓冲区容纳被写入(发送)的值时,发送动作才会阻塞。

有缓冲的channel和无缓冲的channel之间的不同:无缓冲的channel保证进行发送和接收的 goroutine 会在同一时间进行数据交换;有缓冲的channel没有这种保证。

示例

package main

import (
"fmt"
"runtime"
"time"
) var c = make(chan int32, 10) func printstr(s string) {
for _, value := range s {
fmt.Printf("写入%+q\r\n", value)
c <- value
}
} func main() {
runtime.GOMAXPROCS(1)
go func() { printstr("hello")
}() go func() { time.Sleep(time.Second * 2)
fmt.Println("读通道开始读取数据")
for v := range c {
fmt.Printf("读取%+q\r\n", v)
}
}()
for { }
}

结果可以看出,如果给定了一个缓冲区容量,channel就是异步的。只要缓冲区有未使用空间用于发送数据,或还包含可以接收的数据,那么其通信就会无阻塞地进行。

channel的关闭

当发送的一端没有更多的数据发送到channel的话,需要使接收端也能及时知道channel中没有多余的数据可以接收。因此可以通过 close()函数来关闭channel的实现。

示例

package main

import (
"fmt"
"runtime"
"time"
) var c = make(chan int32, 10) func printstr(s string) {
for _, value := range s {
fmt.Printf("写入%+q\r\n", value)
c <- value
}
close(c)
} func main() {
runtime.GOMAXPROCS(1)
go func() {
printstr("hello")
}() time.Sleep(time.Second * 2)
fmt.Println("读通道开始读取数据")
for {
if char, ok := <-c; ok {
fmt.Printf("读取%+q\r\n", char)
} else {
break
}
}
}

提示

  • channel不像文件一样需要经常去关闭,只有当你确实没有任何发送数据了,或者你想显式的结束range循环之类的,才去关闭channel;
  • 关闭channel后,无法向channel 再发送数据(引发 panic 错误后导致接收立即返回零值);
  • 关闭channel后,可以继续从channel接收数据(读取到的数据为channel类型的默认值,如int默认值0 string默认值"");
  • 对于nil channel,无论收发都会被阻塞。

缓冲channel 和 非缓冲channel的区别

  • 缓冲channel的创建方式为make(chan TYPE,CAPCTIY),非缓冲channel的创建方式为make(chan TYPE)
  • 缓冲channel的通信方式为同步通信,非缓冲channel的通信方式为异步通信

单项channel及应用

默认情况下,channel是双向的,既可以往里面发送数据也可以接收数据。但是,常将channel作为参数进行传递而只希望对方是单向使用的,要么只让它发送数据,要么只让它接收数据,这时候可以指定通道的方向。

单项channel的声明

  • 双向channel ch = make(chan int)
  • 单向写channel: var ch chan <- int ch = make(chan <- int)
  • 单向读channel: var ch <- chan int ch = make(<-chan int)

可以将 channel 隐式转换为单向队列,只收或只发,不能将单向 channel 转换为普通 channel,示例:

package main

import (
"fmt"
"runtime"
) var c = make(chan string, 10) func read(c <-chan string) {
fmt.Println("读通道开始读取数据")
for {
if char, ok := <-c; ok {
fmt.Printf("读取%s\r\n", char)
} else {
break
}
}
} func write(ch chan<- string, str []string) {
defer close(ch)
for _, value := range str {
fmt.Printf("写入%+q\r\n", value)
ch <- value
}
} func main() {
runtime.GOMAXPROCS(3) go write(c, []string{"h", "e", "l", "l", "o"})
read(c)
}

golang:Channel协程间通信的更多相关文章

  1. Go 通道(channel)与协程间通信

    协程间通信 协程中可以使用共享变量来通信,但是很不提倡这样做,因为这种方式给所有的共享内存的多线程都带来了困难. 在 Go 中有一种特殊的类型,通道(channel),就像一个可以用于发送类型化数据的 ...

  2. GoLang之协程

    GoLang之协程 目前,WebServer几种主流的并发模型: 多线程,每个线程一次处理一个请求,在当前请求处理完成之前不会接收其它请求:但在高并发环境下,多线程的开销比较大: 基于回调的异步IO, ...

  3. Golang 之协程详解

    转自:https://www.cnblogs.com/liang1101/p/7285955.html 一.Golang 线程和协程的区别 备注:需要区分进程.线程(内核级线程).协程(用户级线程)三 ...

  4. golang:协程安全

    多路复用 Go语言中提供了一个关键字select,通过select可以监听channel上的数据流动.select的用法与switch语法类似,由select开始一个新的选择块,每个选择条件由case ...

  5. 『GoLang』协程与通道

    作为一门 21 世纪的语言,Go 原生支持应用之间的通信(网络,客户端和服务端,分布式计算)和程序的并发.程序可以在不同的处理器和计算机上同时执行不同的代码段.Go 语言为构建并发程序的基本代码块是 ...

  6. 在C++中使用golang的协程

    开源项目cpp_features提供了一个仿golang协程的stackful协程库. 可以在c++中使用golang的协程,大概语法是这样的: #include <iostream> v ...

  7. [Golang]-5 协程、通道及其缓冲、同步、方向和选择器

    目录 协程 通道 通道缓冲 通道同步 通道方向 通道选择器 协程 Go 协程 在执行上来说是轻量级的线程. 代码演示 import ( "fmt" "time" ...

  8. Java-线程间通信

    Java-线程间通信 一 线程通讯 就是多个线程操作同一个资源,可是操作的动作不同 二 停止线程: 控制住run的循环就能够控制线程结束 当线程处于冻结状态,就不会读取标记,线程就不会结束 inter ...

  9. 转:Linux--进程间通信(信号量,共享内存)

    源地址:http://www.cnblogs.com/forstudy/archive/2012/03/26/2413724.html Linux--进程间通信(信号量,共享内存)(转)   一. 信 ...

随机推荐

  1. 【LiteOS】LiteOS消息队列

    目录 前言 链接 参考 笔录草稿 基本概念 队列运作机制 队列运作原理 消息队列传输方式 消息队列的阻塞机制 出队阻塞 入队阻塞 任务相关函数 任务开发流程 注意事项 * 实战 前言 链接 LiteO ...

  2. 第20 章 : GPU 管理和 Device Plugin 工作机制

    GPU 管理和 Device Plugin 工作机制 本文将主要分享以下几个方面的内容: 需求来源 GPU 的容器化 Kubernetes 的 GPU 管理 工作原理 课后思考与实践 需求来源 201 ...

  3. [Fundamental of Power Electronics]-PART II-8. 变换器传递函数-8.3 阻抗和传递函数图形的构建

    8.3 阻抗和传递函数图形的构建 通常,我们可以通过观察来绘制近似的bode图,这样没有大量混乱的代数和不可避免的有关代数错误.使用这种方法可以对电路运行有较好的了解.在各种频率下哪些元件主导电路的响 ...

  4. Kafka核心技术与实战,分布式的高性能消息引擎服务

    Kafka是LinkedIn开发并开源的一套分布式的高性能消息引擎服务,是大数据时代数据管道技术的首选. 如今的Kafka集消息系统.存储系统和流式处理平台于一身,并作为连接着各种业务前台和数据后台的 ...

  5. SQL Server如何将查询的内容保存到新的sql 表中

    我是采用语句将 查询后的数据保存到一个新表中 1)采用into table 语句,需要保存到的这个新表不需要提前创建 select *into NewTable from Table --插入新表的语 ...

  6. selenium启动IE失败,并报错:Unexpected error launching Internet Explorer. Protected Mode settings are not the same for all zones

    1.selenium去启动IE时,报错: Started InternetExplorerDriver server (32-bit)2.50.0.0Listening on port 24641On ...

  7. Mysql之读写分离架构-Atlas

    Atlas介绍 1.png ​ Atlas是由 Qihoo 360, Web平台部基础架构团队开发维护的一个基于MySQL协议的数据中间层项目. 它是在mysql-proxy 0.8.2版本的基础上, ...

  8. Day12_62_线程的生命周期

    线程的生命周期 要实现多线程,必须在主线程中创建新的线程对象. 任何线程一般都具有五种状态,即创建,就绪,运行,阻塞,终止(消亡) 新建状态:在程序中创建了一个新的线程对象后,新的线程对象便处于新建状 ...

  9. 让vim显示空格,tab字符,及vim多行注释

    1.显示 TAB 键 文件中有 TAB 键的时候,你是看不见的.要把它显示出来: :set list 现在 TAB 键显示为 ^I,而 $显示在每行的结尾,以便你能找到可能会被你忽略的空白字符在哪里 ...

  10. Windows下包管理工具Bower的安装和使用

    目录 安装Bower Bower的使用 安装Bower Windows下安装Bower之前,先安装好 nodejs 和 msysgit 环境 然后我们就可以使用npm包管理工具下载并全局安装bower ...