晓夏

北漂的女孩

Good Luck To You!

golang中并发sync和channel

浏览量:445

golang中实现并发非常简单,只需在需要并发的函数前面添加关键字"go",但是如何处理go并发机制中不同goroutine之间的同步与通信,

golang 中提供了sync包和channel机制来解决这一问题.

  1. WaitGroup总共有三个方法:Add(delta int), Done(), Wait()。
    Add:添加或者减少等待goroutine的数量
    Done:相当于Add(-1)
    Wait:执行阻塞,直到所有的WaitGroup数量变成0

例子:

package main

import (
       "fmt"
       "sync"
)

var waitgroup sync.WaitGroup

func Afuntion(shownum int)  {
       fmt.Println(shownum)
       waitgroup.Done()//任务完成,将任务队列的任务数量-1 其实 done 就是add(+1)
}

func main(){
       for i:=1;i<10 ;i++  {
              waitgroup.Add(1)//每创建一个go routine就把队列中的任务数量+1
              go Afuntion(i)
       }
       waitgroup.Wait()//Wait这里会发生堵塞,直到队列所有的任务结束,结束堵塞
}

答案:不一样的数据读出来

使用场景:

  程序中需要并发,需要创建多个goroutine,并且一定要等这些并发全部完成后才继续接下来的程序执行.WaitGroup的特点是Wait()可以用来阻塞直到队列中的所有任务都完成时才解除阻塞,

而不需要sleep一个固定的时间来等待.但是其缺点是无法指定固定的goroutine数目.

Channel机制:

对于channel操作只有4种方式:

  • 创建channel(通过make()函数实现,包括无缓存channel和有缓存channel);

  • 向channel中添加数据(channel<-data);

  • 从channel中读取数据(data<-channel);

  • 关闭channel(通过close()函数实现,关闭之后无法再向channel中存数据,但是可以继续从channel中读取数据)

channel分为有缓冲channel和无缓冲channel,两种channel的创建方法如下:

  • var ch = make(chan int) //无缓冲channel,等同于make(chan int ,0)

  • var ch = make(chan int,10) //有缓冲channel,缓冲大小是5

无缓存channel:

package main

import (
       "fmt"
)

func Afun(ch chan int)  {
       fmt.Println("finish")
       <-ch
}
func main(){
       ch := make(chan int)
       go Afun(ch)
       ch <- 1
}
//输出结果:finish
package main

import (
       "fmt"
)

func Afun(ch chan int)  {
       fmt.Println("finish")
       <-ch
}
func main(){
       ch := make(chan int)
       ch <- 1
       go Afun(ch)
}
//输出结果: 没有结果

代码分析:首先创建一个无缓冲的channel, 然后在主协程里面向channel ch 中通过ch<-1命令写入数据,则此时主协程阻塞,就无法执行下面的go Afuntions(ch),

自然也就无法解除主协程的阻塞状态,则系统死锁

close():
close主要用来关闭channel通道其用法为close(channel),并且实在生产者的地方关闭channel,而不是在消费者的地方关闭.并且

关闭channel后,便不可再想channel中继续存入数据,但是可以继续从channel中读取数据.例子如下:

package main

import "fmt"

func main(){
       var ch = make(chan int, 20)
       for i:=0; i < 10 ;i++  {
              ch <- i
       }
       close(ch)
       for j:= range ch {
              fmt.Println(j);
       }
}

答案:

//输出0 1 2 3 4 5 6 7 8 9

channel阻塞超时处理:
goroutine有时候会进入阻塞情况,那么如何避免由于channel阻塞导致整个程序阻塞的发生那?解决方案:通过select设置超时处理,

具体程序如下:

package main

import (
       "fmt"
       "time"
)

func main() {
       c := make(chan int)
       o := make(chan bool)
       go func() {
              for {
                     select {
                     case i:= <- c:
                            fmt.Println(i)
                     case <-time.After(time.Duration(3)*time.Second):
                            fmt.Println("jieshu")
                            o<-true
                            break
                     }
              }
       }()
       <-o
}


golang 并发总结:
并发两种方式:sync.WaitGroup,该方法最大优点是Wait()可以阻塞到队列中的所有任务都执行完才解除阻塞,但是它的缺点是不能够指定并发协程数量.
channel优点:能够利用带缓存的channel指定并发协程goroutine,比较灵活.但是它的缺点是如果使用不当容易造成死锁;并且他还需要自己判定并发goroutine是否执行完.

但是相对而言,channel更加灵活,使用更加方便,同时通过超时处理机制可以很好的避免channel造成的程序死锁,因此利用channel实现程序并发,更加方便,更加易用.































神回复

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。