goroutine

在go语言中,每一个并发的执行单元叫做一个goroutine

这里说到并发,所以先解释一下并发和并行的概念:

并发:逻辑上具备同时处理多个任务的能力

并行:物理上在同一时刻执行多个并发任务

当一个程序启动时,其主函数即在一个单独的goroutine中运行,一般这个goroutine是主goroutine

如果想要创建新的goroutine,只需要再执行普通函数或者方法的的前面加上关键字go

通过下面一个例子演示并发的效果,主goroutine会计算第45个斐波那契函数,在计算的同时会循环打印:-\|/

这里需要知道:当主goroutine结束之后,所有的goroutine都会被打断,程序就会退出

  1. package main
  2.  
  3. import (
  4. "fmt"
  5. "time"
  6. )
  7.  
  8. func spinner(delay time.Duration) {
  9. for {
  10. for _, r := range `-\|/` {
  11. fmt.Printf("\r%c", r)
  12. time.Sleep(delay)
  13. }
  14. }
  15. }
  16. func fib(n int) int {
  17. //斐波那契数列
  18. if n < {
  19. return n
  20. }
  21. return fib(n-) + fib(n-)
  22. }
  23.  
  24. func main() {
  25. go spinner( * time.Millisecond)
  26. const n =
  27. fibN := fib(n)
  28. fmt.Printf("\rFib(%d)=%d\n", n, fibN)
  29. }

当第一次看到go的并发,感觉真是太好用了!!!!

所以在网络编程里,服务端都是需要同时可以处理很多个连接,我们看一下下面的服务端和客户端例子

服务端:

  1. package main
  2.  
  3. import (
  4. "io"
  5. "log"
  6. "net"
  7. "time"
  8. )
  9.  
  10. func handleConn(c net.Conn) {
  11. defer c.Close()
  12.  
  13. _, err := io.WriteString(c, time.Now().Format("15:04:05\r\n"))
  14. if err != nil {
  15. return
  16. }
  17. time.Sleep( * time.Second)
  18. }
  19.  
  20. func main() {
  21. listener, err := net.Listen("tcp", "localhost:8000")
  22. if err != nil {
  23. log.Fatal(err)
  24. return
  25. }
  26. for {
  27. conn, err := listener.Accept()
  28. if err != nil {
  29. log.Print(err)
  30. continue
  31. }
  32. go handleConn(conn)
  33. }
  34. }

客户端

  1. package main
  2.  
  3. import (
  4. "io"
  5. "log"
  6. "net"
  7. "os"
  8. )
  9.  
  10. func mustCopy(dst io.Writer, src io.Reader) {
  11. // 从连接中读取内容,并写到标准输出
  12. if _, err := io.Copy(dst, src); err != nil {
  13. log.Fatal(err)
  14. }
  15. }
  16. func main() {
  17. conn, err := net.Dial("tcp", "localhost:8000")
  18. if err != nil {
  19. log.Fatal(err)
  20. }
  21. mustCopy(os.Stdout, conn)
  22. }

Channel

channel是不同的goroutine之间的通信机制。

一个goroutine通过channel给另外一个goroutine发送信息。

每个channel 都有一个特殊的类型,也就是channel可以发送的数据的类型

我们可以通过make创建一个channel如:

ch := make(chan int)  这就是创建了一个类型为int的channel

默认我们这样创建的是无缓存的channel,当然我们可以通过第二个参数来设置容量

ch := make(chan int,10)

注意:channel是引用类型,channel的零值也是nil

两个相同类型的channel可以使用==运算符比较。如果两个channel引用的是相通的对象,那么比较的结
果为真。一个channel也可以和nil进行比较。

因为channel是在不同的goroutine之间进行通信的,所以channel这里有两种操作:存数据和取数据,而这里两种操作的

方法都是通过运算符:<-

ch <- x  这里是发送一个值x到channel中

x = <- ch 这里是从channel获取一个值存到变量x

<-ch 这里是从channel中取出数据,但是不使用结果

close(ch) 用于关闭channel

当我们关闭channel后,再次发送就会导致panic异常,但是如果之前发送过数据,我们在关闭channel之后依然可以执行接收操作

如果没有数据的话,会产生一个零值

基于channel发送消息有两个重要方面,首先每个消息都有一个值,但是有时候通讯的事件和发送的时刻也同样重要。

我们更希望强调通讯发送的时刻时,我们将它称为消息事件。有些消息并不携带额外的信息,它仅仅是用做两个goroutine之间的同步,这个时候我们可以用struct{}空结构体作为channel元素的类型

 无缓存的channel

基于无缓存的channel的发送和接受操作将导致两个goroutine做一次同步操作,所以无缓存channel有时候也被称为同步channel

串联的channel (Pipeline)

channel也可以用于多个goroutine连接在一起,一个channel的输出作为下一个channel的输入,这种串联的channel就是所谓的pipeline

通过下面例子理解,第一个goroutine是一个计算器,用于生成0,1,2...形式的整数序列,然后通过channel将该整数序列

发送给第二个goroutine;第二个goroutine是一个求平方的程序,对收到的每个整数求平方,然后将平方后的结果通过第二个channel发送给第三个goroutine

第三个goroutine是一个打印程序,打印收到的每个整数

  1. package main
  2.  
  3. import (
  4. "fmt"
  5. "time"
  6. )
  7.  
  8. func main() {
  9. naturals := make(chan int)
  10. squares := make(chan int)
  11.  
  12. go func() {
  13. for x := ;; x++ {
  14. naturals <- x
  15. }
  16. }()
  17.  
  18. go func() {
  19. for {
  20. x := <-naturals
  21. squares <- x * x
  22. }
  23. }()
  24.  
  25. for {
  26. fmt.Println(<-squares)
  27. time.Sleep( * time.Millisecond)
  28. }
  29. }

但是如果我把第一个生成数的写成一个有范围的循环,这个时候程序其实会报错的。

把for x := 0;; x++改成for x := 0; x < 100; x++,报错如下:fatal error: all goroutines are asleep - deadlock!

所以就需要想办法让发送知道没有可以发给channel的数据了,也让接受者知道没有可以接受的数据了

这个时候就需要用到close(chan)

当一个channel被关闭后,再向该channel发送数据就会导致panic异常

当从一个已经关闭的channel中接收数据,在接收完之前发送的数据后,并不会阻塞,而会立刻返回零值,所以在从channel里接受数据的时候可以多获取一个值如:

  1. go func(){
  2. for {
  3. x ,ok := <-naturals
  4. if !ok{
  5. break
  6. }
  7. squares <- x*x
  8. }
  9. close(squares)
  10. }()
  1. 第二位ok是一个布尔值,true表示成功从channel接受到值,false表示channel已经被关闭并且里面没有值可以接收

单方向的channel

当一个channel作为一个函数的参数时,它一般总是被专门用于只发送或者只接收

chan <- int :表示一个只发送int的channel,只能发送不能接收

< chan int : 表示一个只接受int的channel,只能接收不能发送

当然在有时候我们需要获取channel内部缓存的容量,可以通过内置的cap函数获取

而len函数则返回的是channel内实际有效的元素个数

基于select的多路复用

这里先说一个拥有的知识点:time.Tick函数

这个函数返回一个channel,通过下面代码进行理解:

  1. package main
  2.  
  3. import (
  4. "fmt"
  5. "time"
  6. )
  7.  
  8. func main() {
  9. tick := time.Tick( * time.Second)
  10. for countDown := ; countDown > ; countDown-- {
  11. j := <-tick
  12. fmt.Println(j)
  13. }
  14. }

程序会循环打印一个时间戳

select 语句:

  1. select {
  2. case <-ch1:
  3. // ...
  4. case x := <-ch2:
  5. // ...use x...
  6. case ch3 <- y:
  7. // ...
  8. default:
  9. // ...
  10. }

select语句的形式其实和switch语句有点类似,这里每个case代表一个通信操作

在某个channel上发送或者接收,并且会包含一些语句组成的一个语句块 。

select中的default来设置当其它的操作都不能够马上被处理时程序需要执行哪些逻辑

channel 的零值是nil,  并且对nil的channel 发送或者接收操作都会永远阻塞,在select语句中操作nil的channel永远都不会被select到。

这可以让我们用nil来激活或者禁用case,来达成处理其他输出或者输出时间超时和取消的逻辑

补充:channel 概念

类似unix中的管道pipe

先进先出

线程安全,多个goroutine同时访问,不需要加锁

channel是有类型的,一个整数的channel只能存放

定时器小例子:

  1. //定时器
  2. package main
  3.  
  4. import (
  5. "time"
  6. "fmt"
  7. )
  8.  
  9. func main() {
  10. t := time.NewTicker(time.Second)
  11. for v:= range t.C{
  12. fmt.Println("hello",v)
  13. }
  14. }
  1. // 一次性定时器
  2. package main
  3.  
  4. import (
  5. "time"
  6. "fmt"
  7. )
  8.  
  9. func main() {
  10. select{
  11. case <- time.After(time.Second):
  12. fmt.Println("after")
  13. }
  14. }
  1. //超时控制
    package main
  2.  
  3. import (
  4. "time"
  5. "fmt"
  6. )
  7.  
  8. func queryDb(ch chan int){
  9. time.Sleep(time.Second)
  10. ch <-
  11. }
  12.  
  13. func main() {
  14. ch := make(chan int)
  15. go queryDb(ch)
  16. t := time.NewTicker(time.Second*)
  17. select{
  18. case v:=<-ch:
  19. fmt.Println("result:",v)
  20. case <-t.C:
  21. fmt.Println("timeout")

补充:不同的goroutine之间如何通信

首先我们能够想到的有:全局变量的方式,我们先通过这种本方法来演示:

  1. package main
  2.  
  3. import (
  4. "time"
  5. "fmt"
  6. )
  7.  
  8. var exits []bool
  9.  
  10. func calc(index int){
  11. for i:=;i<;i++{
  12. time.Sleep(time.Millisecond)
  13. }
  14. exits[index] = true
  15. }
  16.  
  17. func main() {
  18. start := time.Now().UnixNano()
  19. go calc()
  20. go calc()
  21. go calc()
  22.  
  23. for{
  24. if exits[] && exits[] &&exits[]{
  25. break
  26. }
  27. }
  28. end := time.Now().UnixNano()
  29. fmt.Println("finished,const:%d ms",end-start)
  30. }

这种方法其实比较笨,go为我们提供了锁同步的方式 sync.WaitGroup,演示代码为:

  1. //等待一组goroutine执行完成
  2.  
  3. package main
  4.  
  5. import (
  6. "time"
  7. "fmt"
  8. "sync"
  9. )
  10.  
  11. var waitGroup sync.WaitGroup
  12.  
  13. func calc(index int){
  14. for i:=;i<;i++{
  15. time.Sleep(time.Millisecond)
  16. }
  17. //执行完成的时候Done
  18. waitGroup.Done()
  19. }
  20.  
  21. func main() {
  22. start := time.Now().UnixNano()
  23. for i:=;i<;i++{
  24. // 每次在调用之前add
  25. waitGroup.Add()
  26. go calc(i)
  27. }
  28. //在循环外等待wait
  29. waitGroup.Wait()
  30. end := time.Now().UnixNano()
  31. fmt.Println("finished,const:%d ms",end-start)
  32. }

补充:关于单元测试和异常捕获

  1. package main
  2.  
  3. import (
  4. "time"
  5. "fmt"
  6. )
  7.  
  8. func calc(){
  9. // defer 定义的后面出现错误的都可以捕获到
  10. defer func() {
  11. err := recover()
  12. if err!=nil{
  13. fmt.Println(err)
  14. }
  15. }()
  16. var p *int
  17. *p =
  18.  
  19. }
转自https://www.cnblogs.com/zhaof/p/8393091.html

golang-goroutine和channel的更多相关文章

  1. golang goroutine的调度

    golang goroutine的调度 1.什么是协程? 协程是一种用户态的轻量级线程. 2.进程.线程.协程的关系和区别: * 进程拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度. ...

  2. goroutine和channel

    近期在学习golang的goroutine和channel时候有一些疑惑: 带缓冲的channel和不带缓冲的channel有什么区别? goroutine和主进程的有哪些影响和关系? 多个gorou ...

  3. Go开发[八]goroutine和channel

    进程和线程 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位. 一个进程可以创 ...

  4. Go--关于 goroutine、channel

    Go--关于 goroutine.channel goroutine 协程是一种轻量化的线程,由Go编译器进行优化. Go协程具有以下特点: 有独立的栈空间 共享程序堆中的空间 调度由用户控制 如果主 ...

  5. go并发之goroutine和channel,并发控制入门篇

    并发的概念及其重要性 这段是简单科普,大佬可以跳过 并发:并发程序指同时进行多个任务的程序.在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行 ...

  6. TODO:Go语言goroutine和channel使用

    TODO:Go语言goroutine和channel使用 goroutine是Go语言中的轻量级线程实现,由Go语言运行时(runtime)管理.使用的时候在函数前面加"go"这个 ...

  7. goroutine 加 channel 代替递归调用,突破递归调用的层级限制

    package main import ( "fmt" "github.com/davecgh/go-spew/spew" "github.com/B ...

  8. Go基础--goroutine和channel

    goroutine 在go语言中,每一个并发的执行单元叫做一个goroutine 这里说到并发,所以先解释一下并发和并行的概念: 并发:逻辑上具备同时处理多个任务的能力 并行:物理上在同一时刻执行多个 ...

  9. [转帖]go 的goroutine 以及 channel 的简介.

    进程,线程的概念在操作系统的书上已经有详细的介绍.进程是内存资源管理和cpu调度的执行单元.为了有效利用多核处理器的优势,将进程进一步细分,允许一个进程里存在多个线程,这多个线程还是共享同一片内存空间 ...

  10. golang的缓冲channel简单使用

    目录 golang的缓冲channel简单使用 阻塞型 非阻塞 golang的缓冲channel简单使用 我们常用的是无缓冲channel : make(chan type) 其实make() 创建c ...

随机推荐

  1. Sort Integers II

    Given an integer array, sort it in ascending order. Use quick sort, merge sort, heap sort or any O(n ...

  2. Hbase(六) hbase Java API

    一. 几个主要 Hbase API 类和数据模型之间的对应关系: 1. HBaseAdmin关系: org.apache.hadoop.hbase.client.HBaseAdmin作用:提供了一个接 ...

  3. Mysql(一) 基本操作

    一.介绍 1.数据库 数据库,通俗的讲,即为存储数据的“仓库”.不过,数据库不仅只是存储,还对所存储的数据做相应的管理,例如,访问权限,安全性,并发操作,数据的备份与恢复,日志等.实际上,我们所提及的 ...

  4. Codeforces Round #441 Div. 2题解

    比赛的时候E调了好久...F没时间写T T A:直接走到短的路上来回走就好了 #include<iostream> #include<cstring> #include< ...

  5. NOIP2010-2015后四题汇总

    1.前言 正式开始的第一周的任务——把NOIP2010至NOIP2015的所有D1/2的T2/3写出暴力.共22题. 暴力顾名思义,用简单粗暴的方式解题,不以正常的思路思考.能够较好的保证正确性,但是 ...

  6. winform登录代码

    Program.cs文件中 static class Program { /// <summary> /// 应用程序的主入口点. /// </summary> [STAThr ...

  7. bzoj 3834 [Poi2014]Solar Panels 数论分块

    3834: [Poi2014]Solar Panels Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 367  Solved: 285[Submit] ...

  8. Linux常用网络工具:批量主机服务扫描之nmap

    Linux下有很多强大网络扫描工具,网络扫描工具可以分为:主机扫描.主机服务扫描.路由扫描等. 之前已经写过常用的主机扫描和路由扫描工具,nmap支持批量主机扫描和主机服务扫描. nmap的安装直接使 ...

  9. MySQL查询和修改auto_increment的方法

    查询表名为tableName的auto_increment值: 复制代码 代码如下: SELECT AUTO_INCREMENT FROM information_schema.tables WHER ...

  10. 在xadmin中自定义内容的变量及优化汇总

    在网上找了很多有关xadmin的内容,发现都不太全 ,找到一篇总结不错的 http://www.lybbn.cn/data/bbsdatas.php?lybbs=62 1.list_display 指 ...