精髓

将资源读进内存-->共享内存,一个个进程/线程进行处理,这是常见模式。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 概述的更多相关文章

  1. Channel概述

    前言 前两篇文章介绍了NIO核心部分部分之一的缓冲区的相关内容,接下来我们继续学习NIO中另一个重要的核心部分--Channel(通道). 在学习这篇文章之前,先做下简单的说明,本文是一篇关于通道的概 ...

  2. go channel 概述 - 管道

    概述 unix/linux OS 的一个进程的输出可以是另一个进程的输入,这些进程使用stdin与stdout设备作为通道,在进程之间传递数据. 同样的,GO中有io.Reader与io.Writer ...

  3. Channel延续篇

    上篇文章中介绍了NIO中的Channel,从Channel是什么.特性.分类几个方面做了下简单的介绍.但是后面Channel的分类,个人感觉不够全面,容易误导读者,特此以这篇文章加以补充. Chann ...

  4. 详解 通道 (Channel 接口)

    在本篇博文中,本人主要讲解NIO 的两个核心点 -- 缓冲区(Buffer) 和 通道 (Channel)之一的 缓冲区(Buffer), 有关NIO流的其他知识点请观看本人博文<详解 NIO流 ...

  5. NIO(二):Channel通道

    一.Channel概述 channel(通道):进行IO的连接通道,为NIO的几个核心(Buffer,selector,channel)之一,相比于IO的stream具有较高的性能. IO 单向传输 ...

  6. NIO类库

    NIO概述 从JDK1.4开始,引入了新的I/O类库,它们位于java.nio包中,其目的在于提高I/O的操作效率.nio是new io的缩写. 参考文章:NIO BIO AIO区别 java.nio ...

  7. SocketChannel简述

    前言 在前面的Channel概述的分类中提到过SocketChannel主要是用来基于TCP通信的通道.这篇文章详细介绍下SocketChannel SocketChannel是什么 SocketCh ...

  8. Java:NIO 学习笔记-3

    Java:NIO 学习笔记-3 根据 黑马程序员 的课程 JAVA通信架构I/O模式,做了相应的笔记 3. JAVA NIO 深入剖析 在讲解利用 NIO 实现通信架构之前,我们需要先来了解一下 NI ...

  9. GO 总章

    GO 学习资源 go 代理 GO 语言结构 GO 数字运算 GO 时间处理 GO 定时器 GO 异常处理 go recover让崩溃的程序继续执行 GO Exit Fatal panic GO 通过进 ...

随机推荐

  1. Git+windows安装教程(一)

    一:Git是什么? Git是目前世界上最先进的分布式版本控制系统. 二:SVN与Git的最主要的区别? SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以 ...

  2. Fiddler抓包工具简介:(四)Fiddler的基本使用

    Fiddler的使用 视图功能区域 会话的概念:一次请求和一次响应就是一个会话. fiddler主界面 下面挑几个快捷功能区中常用几项解释,其他功能自己尝试: 快捷功能区 1:给会话添加备注信息 2: ...

  3. 利用DNS缓存和TLS协议将受限SSRF变为通用SSRF

    本文首发于先知社区 前言 这是今年BlackHat上的一个议题:When TLS Hacks You,作者是latacora的Joshua Maddux 议题提出了一个新的ssrf攻击思路,利用DNS ...

  4. 经过4次优化我把python代码耗时减少95%

    背景交代 团队做大学英语四六级考试相关服务.业务中有一个care服务,购买了care服务考试不过可以全额退款,不过有一个前提是要完成care服务的任务,比如坚持背单词N天,完成指定的试卷. 在这个背景 ...

  5. super和this

    super注意点: 1.super调用父类的构造方法,必须在构造方法的第一个 2.super必须只能出现在子类的方法或者构造方法中 3.super和this不能同时调用构造方法 this: 代表的对象 ...

  6. JAVA学习(六)

    今天先是把内存知识总结归纳地又学习了一遍,现在可以很清楚地描述JVM的内存是如何操作的了. 静态变量储存在方法区内存中,这个之前没有注意到,温故知新了. 如果一个引用是空的(就是指向null),那它在 ...

  7. 说下我费了几个钟头才搞定的myeclipse和tomcat问题

    配置myeclipse与tomcat的时候,我根本没有想到myeclipse已经集成了tomcat,根据我上篇文章可以找到所集成的tomcat的位置.于是,自己下了一个tomcat,也许是自作聪明吧, ...

  8. [cf1305G]Kuroni and Antihype

    对整个过程构造一张有向图,其中$(x,y)\in E$当且仅当$x$把$y$加入,且边权为$a_{x}$ 显然这是一棵外向树森林,并再做如下两个构造: 1.新建一个点$a_{0}=0$,将其向所有入度 ...

  9. [cf1340D]Nastya and Time Machine

    记$deg_{i}$为$i$的度数,简单分类讨论可得答案下限为$\max_{i=1}^{n}deg_{i}$ 另一方面,此下限是可以取到的,构造方法较多,这里给一个巧妙一些的做法-- 对其以dfs(儿 ...

  10. [atAGC001F]Wide Swap

    结论:排列$p'_{i}$可以通过排列$p_{i}$得到当且仅当$\forall 1\le i<j<i+k,(p_{i}-p_{j})(p'_{i}-p'_{j})>0$ 证明:构造 ...