go语言实战读书笔记之 goroutine

goroutine

go语言并发: 能让某个函数独立于其他函数运行的能力

当一个函数创建为goroutine时,go将它视为一个独立的工作单元,这个工作单元会被调度到可用的逻辑处理器上执行

go语言运行时调度器: 能管理被创建的所有goroutine并为其分配执行时间,
调度器在操作系统之上,将操作系统的线程和语言运行时的逻辑处理器绑定,并在逻辑处理器上运行goroutine

go语言的并发同步模型: 叫通信顺序进程(communicating Sequential Processes)CSP的范性,CSP是一种消息传递模型,通过在goroutine之间传递数据来传递消息,而不是对数据进行加锁来实现同步访问.
用于在goroutine之间同步和传递数据的关键数据类型叫做通道(channel)

并行: 是让不同的代码片段同时在不同的物理处理器上执行,关键是同时做很多事情

并发: 是指同时管理很多事情,这些事情可能只做了一半就被暂停去做别的事情

下图是一个逻辑处理器上并发运行goroutine和两个逻辑处理器上并行运行两个并发的goroutine
在这里插入图片描述

进程: 包含了应用程序运行需要用到和维护的各种资源的容器,这些资源包括但不限于内存地址空间,文件和设备的句柄以及线程
在这里插入图片描述

线程:一个执行空间,这个空间被操作系统调度来运行函数中所写的代码

每个进程至少包含一个线程

操作系统会在物理处理器上调度线程来运行
go语言运行时会在逻辑处理器上调度goroutine来运行
每个逻辑处理器都分别绑定在单个操作系统线程上

如果创建一个goroutine并准备运行:
这个goroutine就会被放在调度器的全局运行队列中;
调度器就将这些队列中的goroutine分配一个逻辑处理器,并放在这个逻辑处理对应的本地运行队列中;
本地运行队列中goroutine一直等待自己被分配到逻辑处理器上执行

正在运行的goroutine执行一个阻塞的系统调用:
线程和goroutine会从逻辑处理器上分离;
该线程继续阻塞等待系统调用的返回;
这个逻辑处理器就失去了运行的线程,调度器会新建线程,并将其绑定到该逻辑处理器上;
调度器会从本地运行队列中选择另外的goroutine来运行;
一旦被阻塞的系统调用执行完成并返回,对应的goroutine会放回到本地运行队列,之前的线程会保存好以遍之后使用
在这里插入图片描述

如果一个goroutine需要做一个网络I/O调用:
goroutine会和逻辑处理器分离,并移到集成了网络轮询器的运行时;
一旦该轮询器指示某个网络读或者写操作已经就绪,对应的gorouotine会重新分配搭配到逻辑处理器上来完成操作.

调度器对可以运行的逻辑处理器没有限制
语言运行时默认限制每个程序最多创建10000个线程,此限制可以通过调用runtime/debug包中的SetMaxThread方法来更改,如果程序试图使用更多的线程就会崩溃

创建两个goroutine,以并发的形式分别显示大写和小写的英文字母

func main(){
    runtime.GOMAXPROCS(1) //分配一个逻辑处理器给调度器使用
    var wg sync.WaitGroup
    wg.Add(2).  //计数器加2,表示要等待两个goroutine
    fmt.println("Start Goroutine")
    go func(){
        defer wg.Done()
        for i:=0;i<3;i++{
          for char:='a';char<'a'+26;char++{
           fmt.printf("%c",char)
           }
        }
    }()
       go func(){
        defer wg.Done() //通知main函数工作已完成
        for i:=0;i<3;i++{
          for char:='A';char<'A'+26;char++{
           fmt.printf("%c",char)
           }
        }
    }()
   fmt.println("Waiting To Finish")
   wg.wait()
}

WaitGroup:是一个计数信号量,可以用来记录并维护运行的goroutine.
如果waitgroup大于0,wait方法就会阻塞

defer:会修改函数调用时机,在正在执行的函数返回时才真正调用defer声明的函数

只有在有多个逻辑处理器,且可以同时让每个goroutine运行在一个可用的物理处理器上时,goroutine才会并行运行.

竞争状态

两个或多个goroutine试图访问同一个资源

手动让一个goroutine从线程退出并放回队列的方法
runtime.Gosched()

检测竞争状态方法: 用竞争检测器标志来编译程序
go build -race

防止出现竞争状态:
1、原子函数以很低层的加锁机制来同步访问整形变量和指针
atomic.AddInt64()
atomic.StoreInt64()
atomic.LoadInt64()
2、互斥锁用于在代码上创建一个临界区,保证同一时间只有一个goroutine可以执行临界区代码;
sync.Mutex, mutex.Lock(),mutex.UnLock()
3、通道,通过发送和接收需要共享的资源,在goroutine之间做同步

通道

通道提供了一种在两个goroutine之间共享数据的简单方法

无缓冲通道: 接收前没有能力保存任何值的通道,要求发送gproutine和接收goroutine同时准备好,通道会导致先执行发送或接收操作的goroutine阻塞等待
有缓冲通道: 被接收前能缓冲多个值的通道,此通道并不要求goroutine同事完成发送和接收
阻塞发送的条件:只有在通道中没有缓冲区容纳发送的值时,发送动作才会阻塞
阻塞接收的条件:只有在通道中没有要接收的值时,接收动作才会阻塞
所以无缓冲的通道保证同时交换数据,而有缓冲的通道不做这种保证

有缓冲通道:当通道关闭后,goroutine依旧可以从通道接收数据,不能再向通道发送数据,从已关闭通道接收数据很重要,因为这允许通道关后依旧能取出其中缓存的全部值,而不会丢失数据;
从一个已关闭但没有数据的通道里获取数据,总会立即返回,并返回一个通道类型的零值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值