Go 并发

Wesley13
• 阅读 898

Go 并发

  • 并发指的是同时处理多个任务的能力。
  • 并行指的是并行处理多个任务的能力。

并行不一定加快运行速度,因为并行组件之间可能需要互相通信。

Go中使用协程,信道来处理并发。

协程

Go中主要通过协程实现并发。

协程是与其他函数或方法一起并发运行的函数或方法,协程可以看作是轻量级线程,但是创建成本更小,我们经常会看见数以千计的协程并发运行。

协程相比线程的优势

  • Go协程创建成本低,堆栈大小只要几K,并可以根据应用需要进行递减,线程必须指定堆栈大小,堆栈是固定不变的。
  • Go中多个协程会复用数量更少的OS线程,就是可能数千个协程公用一个线程。如果线程中一个协程阻塞,OS会再创建一个线程,并把其他的协程复制到新的线程上。
  • Go通过Channel来进行协程间通信,Channel防止多个协程访问共享内存时发生竞态条件,信道是Go协程间通信的管道。

创建协程

调用函数或者方法时,在前面加上关键字go可以运行在新协程上。

func hello(){
}

func main(){
    go hello()
}

Main函数运行在主协程上,hello并发的运行在新协程上。

我们启动一个新协程,这个新协程会立即返回,而不会等到函数或方法执行完毕。 如果主协程终止,其他协程也会终止。

我们可以在主协程中使用休眠阻塞主协程,等待协程执行完毕。

信道(Channel)

信道是协程间通信的管道。

每个信道都关联一个类型,信道只能运输这种类型的数据,传输其他类型数据是违法的。

chan T 表示T传输类型的信道

可以使用make来定义信道。

a := make(chan int)

通过信道进行发送和接收数据

data : = < - a //读取信道
a <- data // 写入信道

通过信道旁边的箭头指定是发送数据还是接收数据。

信道的发送和接收默认是阻塞的

当把数据发送到信道时,发送数据语句发生阻塞直到其他Go协程从信道读取到数据才会解除阻塞。

同样,当读取信道数据时,如果没有其他协程把数据写入信道,读取过程会一直阻塞。

Go协程和信道特性没有像其他语言需要加锁和同步的开销。

func hello(done chan bool){
    done < - true // 向信道中写入数据
}

func main(){
    done := make(chan bool)
    go hello(done)
    < - done // 接收数据,会发生阻塞
}

接收多个信道值:

s, c := <-sq, <-cu

死锁

信道使用过程中需要考虑的重点是死锁,当协程给一个信道发送数据时,按理说其他协程会来接收数据,如果没有的话,会形成死锁。 同样等的从信道读取数据是,也会产生死锁。

单向信道

上面的都是双向信道,即通过信道既能发送数据也能接受数据,我们也可以创建单向信道,只能发送或者接受数据。

sendch := make(chan<-int) // 创建了只能发送数据的单项信道

当我们尝试从此信道读取数据时,会报错。

Go中可以把双向信道转换成单向信道这样单向信道能正常读写,但是不能把单向信道转成双向信道。

关闭信道和遍历信道

数据发送方可以关闭信道,通知接收方这个信道不再产生新数据。

检查信道是否关闭:

v, ok := <- ch // 接收数据,同时用另一个变量检查关闭状态

如果ok为false,说明通道关闭,得到的值为零值。

func producer(chnl chan int){
    for I := 0; I < 10; I++{
        chill <- I // 循环0~9写入信道
    }
    close(chnl) // 关闭信道 
}

func main(){
    ch := make(chan int)
    go producer(ch)
    for{ // 主函数死循环
        v, ok := <-ch
        if ok == false{ // 检查到信道关闭
            break
        }
    }
}

缓冲信道

上面的都是无缓冲信道,接收和发送数据过程都是阻塞的。

缓冲信道,只有在缓冲满的情况下才会阻塞缓冲信道发送。只有在缓冲为空的时候,才会阻塞信道获取。

通过make函数传递一个容量参数,代表缓冲大小,可以创建缓冲信道。

ch := make(chan type, capacity)

capacity容量应该大于0,无缓冲信道默认容量为0。

同样当缓冲信道堵塞到最大容量时也会产生死锁。

信道长度和容量

容量代表信道可以存储的值数量,使用make函数创建缓冲信道时候会指定容量大小。

缓冲信道长度指的是信道中当前队列元素个数。

点赞
收藏
评论区
推荐文章
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
HelloWorld官方 HelloWorld官方
4年前
Go Context 并发编程简明教程
1为什么需要ContextWaitGroup和信道(channel)是常见的2种并发控制的方式。如果并发启动了多个子协程,需要等待所有的子协程完成任务,WaitGroup非常适合于这类场景,例如下面的例子:varwgsync.WaitGroupfuncdoTask(nint){time.Sleep(time.Durat
GO语言协程的理解
以下内容大部分摘自许世伟的《GO语言核心编程》最近面试,在自己的简历上写了简单会一些GO语言。结果被面试官问了GO语言goroutine的原理。自己看倒是看过,时间长了又给忘了。特此写下此文以长记性。协程:协程本质上是一种用户态线程,不需要操作系统来进行抢占式调度,并且在真正的实现中寄存于线程中,因此系统开销极小,可以有效的提高线程任务的并发性,而避
Stella981 Stella981
3年前
Gevent简明教程
1、前述进程线程协程异步并发编程(不是并行)目前有四种方式:多进程、多线程、协程和异步。多进程编程在python中有类似C的os.fork,更高层封装的有multiprocessing标准库多线程编程python中有Thread和threading异步编程在linux下主要有三种实现selec
Stella981 Stella981
3年前
Go并发(五):WaitGroup
本篇其实是Go并发编程的补充篇章,只是用来介绍WaitGroup的使用介绍。对于Go的并发编程来说,主协程需要知道其他的协程何时结束。通常我们的做法是采用channel的方式来控制,这种做法很好,可以参见我的另外一个帖子(Go并发之channel篇:https://mp.weixin.qq.com/s/PIbgGBootc6581pHhi5ew(h
Wesley13 Wesley13
3年前
Go 中的并发和并行
并行是指同一时间做多件事情,并发是指同一时间具有做多件事情的能力。在很多情况下,并发的效果比并行好,因为操作系统的硬件资源和总资源是很少而且固定的,不能无限去扩张。Go语言设计的时候应该也是推崇这种使用较少资源做更多事情的哲学。并发执行Go语言中可以使用系统功能设置可以使用的物理处理器(核),如果设置为1的话,所有的协程会在一个核上
Easter79 Easter79
3年前
Swoole2.0内置协程并发测试
Swoole2.0是一个革命性的版本,它内置了协程的支持。与Go语言协程不同,Swoole协程完全不需要开发者添加任何额外的关键词,直接以过去最传统的同步阻塞模式编写代码,底层自动进行协程调度实现异步IO。使并发编程变得非常简单。最新的版本中,内置协程已支持PHP7,同时兼具了性能和并发能力,Swoole的强大超乎想象。本文基于Github最新的Sw
Stella981 Stella981
3年前
Golang学习笔记:goroutine
1.goroutinegoroutine是go语言的并发体。在go语言里面能使用go关键字来实现并发。gofunc()1.1概念介绍goroutine本质上是协程,我刚刚学习的时候就粗略地认为goroutine是线程,直到最近才开始搞明白goroutine的基本概念。<fon
使用asyncio库和多线程实现高并发的异步IO操作的爬虫
摘要:本文介绍了如何使用Python的asyncio库和多线程实现高并发的异步IO操作,以提升爬虫的效率和性能。通过使用asyncio的协程和事件循环,结合多线程,我们可以同时处理多个IO任务,并实现对腾讯新闻网站的高并发访问。正文:在网络爬虫中,IO操作