
goroutine我们从调度上声明线程与goroutine的区别cpu已经通过分配时间,自带调度器实现切换时间片帮我们解决了多程序(任务)执行问题,在此基础上推演出更小单位多线程: 多线程的执行依赖os(操作系统)的调度分配操作系统促使硬件调度时钟隔个一段时间发送一个信号到cpu中cpu结束当前执行线程的函数(程序)并将执行信息从寄存器保存到内存中再查看线程清单中接下来要继续执行的线程(执行过程:内存中取出来发到寄存器(上下文切换)),并执行。以这种方式不断进行线程切换执行/恢复/切换/销毁 (1.操作系统本身也会增加cpu负载,再加上硬件调度,属于三方调度,降低效率 2.cpu-寄存器速度大概是内存的100倍,如此切换执行,费时)上面是大众的多线程运行方式我们来看一下goroutine的运行原理,go是一种类似于c系语言编程众多的包,编译的速度,二进制的直接执行文件,使得go开发效率极高。go运行于goroutine中,cpu运行时本身自带线程调度器(程序调度器)使用一个go关键字即可实现多线程运行但是并不是对应一个物理线程假设调度器中有两个参数 A:B ,代码中goroutine数量为 A,物理线程为 B,(B的数量可配置,最好根据cpu核数来定义,此处不深究)动态自动的分配,销毁降低了开发成本(锁的概念见下文)在goroutine中通过异步/阻塞来挂起线程降低了线程切换成本内存上线程运行要涉及内存保存信息,每一个线程都分配一个MB级别的内存存储信息。注意此处是固定很多’线程时MB单位存储对内存是一种浪费,这就意味着限制了编程中线程开辟数量浪费了cpu/内存资源。goroutine是’动态’分配大小重要的是它的单位是KB,你可以忽略线程的消耗尽情的创建(此处不严谨的猜想:线程可挂起并通过通道传递信息不难发现按需增大缩小正是动态分配的关键通讯)大家可能明白协程为啥这么畅销码农市场了我们来搞几个demo玩一下之前参考了几篇go语言中文社区的文章发现人家的讲解已经很清晰了所以这里我计划多一些我自己的理解(偏理论)的东西go协程使用需要用到的几种通讯方式有 sync,channel … (不下三种,水平有限,只会channel),go的官网文档介绍[sync:大部分都是适用于低水平程序线程高水平的同步使用channel通信更好一些。],channel也是golang的最为核心的技术(论程序员的修养:channal是google的大佬底层封装的好)阻塞/挂起线程类似于锁的概念或者说php/js中断点只不过是可通过通讯方式来开启/关闭有缓冲channel声明方式ch : make(chan int 3)运行方式’异步’, 仅限不阻塞情况下异步执行,阻塞后还是同步等待执行,第二个参数为缓冲的大小使用1. 通道的缓存无数据但执行读通道。2. 通道的缓存已经占满向通道写数据但无协程读。在缓冲范围内,两个goroutine不阻塞异步执行通过channel传输数据超过缓冲长度时会阻塞等待消费者消费数据消费后阻塞释放无缓冲通道的读取方式:通过range 类迭代方式读取但此处要注意的是range不会根据通道的长度来作为循环的次数而是根据通道是否关闭来限定次数,如不关闭通道,无线循环取数据会发生阻塞等待,发展成死锁。ch:make(chanint,3)ch-7ch-8ch-9close(ch)// 关闭通道foritem:rangech{fmt.Println(item)}// stystem out : 7 8 9 //遵循先进先出顺序// 或者通过 人工 break 跳出循环ch:make(chanint,3)ch-7ch-8ch-9foritem:rangech{fmt.Println(item)iflen(ch)0{break// 人工跳出循环}}无缓冲channel声明方式ch : make(chan int)运行方式’同步’使用情况1. 通道中无数据但执行读通道。2. 通道中无数据向通道写数据但无协程读取。生产消息/消费消息(一下成为存/取)是原子性每一个动作都会阻塞当前线程挂起线程并非销毁,一直等待对应的存/取操作发生来解锁,这也就意味着多数情况下使用无缓冲通道必须要开辟两个goroutine,一个存一个取只要存了就必须要取出来不然会无限阻塞形成死锁。所以无缓冲channel中len永远是0;[阻塞:1.存进去就必须要取出来 2.如果本来就没数据去也可以取但是要一直阻塞等待到其它goroutine存入数据]示例方法为省代码量,全用闭包代替(主要是一些死锁情况)存入无缓冲通道此处触发阻塞,下面goroutine根本没机会执行,导致无线阻塞等待数据消费,形成死锁xChan:make(chanint)xChan-1gofunc(){// code ...-xChan}()fmt.Println(main done.. no deadlock)// fatal error: all goroutines are asleep - deadlock!换个位置,再来一次阻塞位置变化直到子线程取出数据,阻塞释放(串行编程,没招)xChan:make(chanint)gofunc(){// code ...-xChan}()xChan-1fmt.Println(main done.. no deadlock)// main done.. no deadlock代码竟然没报错,这是因为主线程没进行读/取操作不存在阻塞,根本就等到子线程执行就执行完了(所有的线程的生命周期都是伴随着主线程的终止而终止,调度器和cpu就算再强大,开辟线程也是需要时间的)c:make(chanint)gofunc(){c-1}()fmt.Println(main done.. no deadlock)这个例子是说无缓冲channel要伴随着两个goroutine搭配,如果换成有缓冲通道,在一个goroutine就不会报错因为是异步的类似于queque但也是失去了并发编程的意义ch:make(chanint)// 写入数据堵塞当前线程, 没人取走数据阻塞不会释放ch-1//在此行执行之前Go就会报死锁fmt.Println(main done.. no deadlock)多channel管理可用 select 语句进行管理可通过设置default方式避免死锁发生无缓冲的信道是数据一个进一个出交替阻塞执行有缓冲信道则是一个一个存储一个个取出异步队列不阻塞执行