go channel 概述
精髓
将资源读进内存-->共享内存,一个个进程/线程进行处理,这是常见模式。go channel 是一种直接在进程/线程之间传递资源的方式,即以通信来共享内存。这便是go的精髓。
扩展-一些名词了解
Linux、IPC(进程间通信)、进程(六个状态)、线程、同步、异步、信号、管道、socket、消息队列、字节流、结构化消息、通信、信号量、共享内存、内核空间、用户空间、PID、PPID、fork、COW
将进程细化为多个状态后,系统CPU就可以更加灵活地调度,进程下还有更细的线程,有N多状态,
GO调度器实现一套机制,统一调度与分配这些CPU、进程、线程等资源,分为M(内核)、P(go上下文)、G(goroutine)三层
以上概念不学linux原理的话,听个名词就好,GO中重点学习两个点
1. 第三层G就是我们经常使用的goroutine协程,掌握goroutine的使用
2. GO支持的IPC方法有 信号、管道、socket,掌握这三种的实现方式
1. 掌握goroutine的使用
---------------------------------------------------------------------------------------------------------
定义
chan T 双向
chan<-T 只发送
<- chan T 只接收
通道类型,也是引用类型,零值为nil
特性
同一时刻,仅有一个goroutine可以向该通道发送元素值,同时也仅有一个goroutine可以从该通道接收元素值,即通道是串行的。
通道中的元素值,严格按发送到该通道的先后顺序排列,最先发送到的元素值,一定最先被接收。等效于先进先出的消息队列。
通道中的元素值,具有原子性,不可被分割;每个元素值只能被一个goroutine接收,被接收后,立刻从通道中清除。
goroutine的执行与主程序是并行的,主程序结束时,还没有执行完毕的goroutine会被强制中止。
通道的传值是复制,不是引用。
通道初始化
通道未初始化时其值为nil,可以从nil中尝试接收元素,但会被永远阻塞
make(chan int)
初始化一个可以接收、发送int值类型的通道,无缓冲
make(chan int, 10)
初始化一个可以接收、发送int值类型的通道,可缓冲10个int值。第11个int值向通道中发送时会被阻塞。
被缓冲的元素值,会严格按发送的顺序接收。
从通道中接收元素
c := make(chan int, 10)
n := <- c
如果通道 c 被关闭,那么n的值为该元素类型的零值,本例int类型的零值为0;如果是在接收的过程中被关闭了,n的值同样为0。
package main
import "fmt"
func main() {
c:= make(chan int,10)
close(c)
n:=<-c
fmt.Println(n)
}
n,ok := <- c
这种写法与上面的唯一区别在于,当通道关闭时,ok的值为false
package main
import "fmt"
func main() {
c:= make(chan int,10)
close(c)
n,ok:=<-c
fmt.Println(n,ok) // 0 false
}
让后台协程有序执行
本例很好地诠释了前面的概念描述。tools包是个人自定义包,尝试运行时,可自行修改一下代码,个人为了方便就不再更改本地环境代码了。
package main import (
"sync"
"time"
"tools"
) func main() {
startTask()
} func startTask() { cmdList := make([]string,6)
cmdList[0] = "mkdir -p /opt/test/dir1"
cmdList[1] = "mkdir /opt/test/dir1/dir2"
cmdList[2] = "mkdir /opt/test/dir1/dir2/dir3"
cmdList[3] = "mkdir /opt/test/dir1/dir2/dir3/dir4"
cmdList[4] = "mkdir /opt/test/dir1/dir2/dir3/dir4/dir5"
cmdList[5] = "mkdir /opt/test/dir1/dir2/dir3/dir4/dir5/dir6" var cmdLength = len(cmdList)
var cmdChan = make(chan string,cmdLength) var onOff = make(chan int,cmdLength) var wg sync.WaitGroup
wg.Add(cmdLength)
for i,cmd := range cmdList {
cmdChan <- cmd
go task(&cmdChan,&onOff,&wg,i,cmdLength)
}
//启动第一个任务,第一个任务结束时会启动第二个任务,依次类推
onOff <- 0
wg.Wait()
} func task(cmdChan *chan string,onOff *chan int,wg *sync.WaitGroup, seq,maxLength int) { for {
if i,ok := <- *onOff; ok {
if i == seq {
//跳出循环等待,开始当前顺序命令执行
break
}else {
//将取出的命令执行序号放回通道,然后暂停一段时间该后台协程
*onOff <- i
//让不该执行的后台协程等待,就会让该执行的后台线程更容易获取执行机会,因为它不用等
time.Sleep(10*time.Microsecond) }
}
} //从缓冲中取出的元素顺序,严格遵从放入时的顺序
//所以取出的顺序一定会按命令列表的下标从0排列到列表结束
if cmd,ok := <- *cmdChan; ok{
tools.ExecShell(cmd)
}
wg.Done() seq++
if seq < maxLength{
*onOff <- seq
} }
linux mkdir有个特性,其父目录若不存在,则无法创建子目录;若命令不按列表顺序执行,那么最后的目录肯定不会创建成功,
若成功创建所有目录,则表明命令是按顺序执行的。
若去掉onOff顺序控制,本例就是一个后台并发执行的例子。
关闭通道
通常在发送端关闭通道
不可重复关闭通道,关闭一个已经关闭的或未初始化的通道会引发异常
通道关闭后,其中未接收的数据仍可被接收
接收端应先判断通道是否关闭再从中取值,否则若通道关闭可能取出的值是通道类型的零值
package main
import "fmt"
func main() {
dataChan := make(chan int,3)
startChan := make(chan string,1)
overChan := make(chan string,2)
go func() {
<- startChan
fmt.Println("start receive data ")
for{
if elem,ok:= <- dataChan; ok{
fmt.Printf("%v\n",elem)
}else {
break
}
}
fmt.Println("reciver over")
overChan <- "rec over"
}()
go func() {
for i:=0;i<3;i++ {
dataChan <- i
fmt.Sprintf("send data:%v\n",i)
}
fmt.Println("send data over")
//在接收之前关闭通道
close(dataChan)
fmt.Println("dataChan closed")
//开始接收
startChan <- "begin"
overChan <- "send data over"
}()
<- overChan
<- overChan
fmt.Println("main over")
}
长度与容量
长度是通道中元素的个数,非固定值
容量是通道中可能缓存的元素个数
package main
import "fmt"
func main() {
c1 := make(chan int,5)
fmt.Printf("c1 长度:%v, 容量:%v\n",len(c1),cap(c1))
c2 := make(chan int)
fmt.Printf("c2 长度:%v, 容量:%v\n",len(c2),cap(c2))
}
c1 长度:0, 容量:5
c2 长度:0, 容量:0
单向通道
chan T 双向 用于channel定义
chan<-T 只发送 用于接口、函数参数定义
<- chan T 只接收 用于接口、函数参数定义
双向通道可以转换为发送/接收通道,反之不可以。
for 与 channel
可以从未初始化的通道(nil)中取值,但会被阻塞
当通道关闭时,会将通道中的元素全部取出后,语句结束
必须是一个双向通道或接收通道,不是只是发送通道。
var c3 chan int
for elem := range c3 {
fmt.Println(elem)
}
Linux进程
linux进程通过调用系统函数fork创建,首个系统进程为/usr/bin/sbin,其他进程都是该进程的子进程,呈现树状结构。
进程创建方式:复制父进程的数据段、堆、栈等数据,共享父进程的代码段,复制后子进程、父进程相互间是独立的。系统内核通过写时复制(copy on write,COW)来提高创建进程的效率。
Linux进程详细的结构如下,大致分为四部分 代码区(Text segment)-只读静态区,Data段,Heap段,Stack段。

进程状态图

注意这里的僵尸状态,是不是僵尸状态是子进程反馈给父进程的,如果子进程自己发生意外,无法给父进程反馈但还一直存在着,那么子进程的状态可能会成为僵尸状态
SIGCHILD只是在子进程退出的时候发送给父进程的一个信号值,这是一种异步通知父进程的方式.父进程可以捕获,忽略这个信号,默认动作是忽略此信号.
常用的使用方式是,当SIGCHILD信号发生时候,主进程在SIGCHILD的信号处理函数中调用waitpid or wait来回收子进程的结束状态。但需要明白的是:waitpid or wait不是依靠SIGCHLD信号是否到达来判断子进程是否结束,(可能是通过轮巡检测子进程状态来判断的,需要看具体代码实现才能确定),即wait/waitpid并不依赖于SIGCHILD信号.常常在SIGCHILD的信号处理函数中调用 wait/waitpid回收子进程状态是为了避免wait/waitpid不必要的轮巡,是属于一种节约资源的方式,但这并不是必须的。
未完.....
go channel 概述的更多相关文章
- Channel概述
前言 前两篇文章介绍了NIO核心部分部分之一的缓冲区的相关内容,接下来我们继续学习NIO中另一个重要的核心部分--Channel(通道). 在学习这篇文章之前,先做下简单的说明,本文是一篇关于通道的概 ...
- go channel 概述 - 管道
概述 unix/linux OS 的一个进程的输出可以是另一个进程的输入,这些进程使用stdin与stdout设备作为通道,在进程之间传递数据. 同样的,GO中有io.Reader与io.Writer ...
- Channel延续篇
上篇文章中介绍了NIO中的Channel,从Channel是什么.特性.分类几个方面做了下简单的介绍.但是后面Channel的分类,个人感觉不够全面,容易误导读者,特此以这篇文章加以补充. Chann ...
- 详解 通道 (Channel 接口)
在本篇博文中,本人主要讲解NIO 的两个核心点 -- 缓冲区(Buffer) 和 通道 (Channel)之一的 缓冲区(Buffer), 有关NIO流的其他知识点请观看本人博文<详解 NIO流 ...
- NIO(二):Channel通道
一.Channel概述 channel(通道):进行IO的连接通道,为NIO的几个核心(Buffer,selector,channel)之一,相比于IO的stream具有较高的性能. IO 单向传输 ...
- NIO类库
NIO概述 从JDK1.4开始,引入了新的I/O类库,它们位于java.nio包中,其目的在于提高I/O的操作效率.nio是new io的缩写. 参考文章:NIO BIO AIO区别 java.nio ...
- SocketChannel简述
前言 在前面的Channel概述的分类中提到过SocketChannel主要是用来基于TCP通信的通道.这篇文章详细介绍下SocketChannel SocketChannel是什么 SocketCh ...
- Java:NIO 学习笔记-3
Java:NIO 学习笔记-3 根据 黑马程序员 的课程 JAVA通信架构I/O模式,做了相应的笔记 3. JAVA NIO 深入剖析 在讲解利用 NIO 实现通信架构之前,我们需要先来了解一下 NI ...
- GO 总章
GO 学习资源 go 代理 GO 语言结构 GO 数字运算 GO 时间处理 GO 定时器 GO 异常处理 go recover让崩溃的程序继续执行 GO Exit Fatal panic GO 通过进 ...
随机推荐
- c++ 算法 next_permutation
遇到这个算法是在大牛写的10行的8皇后问题中,下面首先给出这个10行就解决了8皇后的NB代码,我目前还是没有看懂对于皇后不在同一列的判断,因为他巧妙的用了移位操作. #include<iostr ...
- 第2章-7 产生每位数字相同的n位数 (30分)
第2章-7 产生每位数字相同的n位数 (30分) 读入2个正整数A和B,1<=A<=9, 1<=B<=10,产生数字AA-A,一共B个A 输入格式: 在一行中输入A和B. 输出 ...
- PTA 7-1 邻接矩阵表示法创建无向图 (20分)
PTA 7-1 邻接矩阵表示法创建无向图 (20分) 采用邻接矩阵表示法创建无向图G ,依次输出各顶点的度. 输入格式: 输入第一行中给出2个整数i(0<i≤10),j(j≥0),分别为图G的顶 ...
- RabbitMQ 线上事故!慌的一批,脑袋一片空白。。。
前言 那天我和同事一起吃完晚饭回公司加班,然后就群里就有人@我说xxx商户说收不到推送,一开始觉得没啥.我第一反应是不是极光没注册上,就让客服通知商户,重新登录下试试.这边打开极光推送的后台进行检查. ...
- 问题 A: 喷水装置(一)
题目描述 现有一块草坪,长为20米,宽为2米,要在横中心线上放置半径为Ri的喷水装置, 每个喷水装置的效果都会让以它为中心的半径为实数Ri(0<Ri<15)的圆被湿润,这有充足的喷水装置i ...
- 博主日常工作中使用的shell脚本分享
前言: 今天给大家分享一篇在我工作中常用的一个shell脚本,里面有一些我们常用到的shell操作.该脚本用于本地电脑和服务器交互上,实现以下功能: 自动拉取自己个人电脑上的源码到服务器上yocto包 ...
- k8s网络模型与集群通信
在k8s中,我们的应用会以pod的形式被调度到各个node节点上,在设计集群如何处理容器之间的网络时是一个不小的挑战,今天我们会从pod(应用)通信来展开关于k8s网络的讨论. 小作文包含如下内容: ...
- [bzoj1982]Moving Pebbles
首先发现当n堆石子可以两两配对时,后手必胜,因为后手可以模仿先手那么当n堆石子不能两两配对时,先手必胜,因为先手可以做到让其两两配对,然后即先手必胜 这个东西用map维护即可 1 #include&l ...
- dart系列之:在dart中使用生成器
目录 简介 两种返回类型的generator Stream的操作 总结 简介 ES6中在引入异步编程的同时,也引入了Generators,通过yield关键词来生成对应的数据.同样的dart也有yie ...
- Java遍历map的五种方式
使用For-Each迭代entries 这是最常见的方法,并在大多数情况下更可取的.当你在循环中需要使用Map的键和值时,就可以使用这个方法 Map<Integer, Integer> m ...