【Go学习笔记12】并发(二)

WaitGroup介绍

在go语言中,WaitGroup是用来表示一组等待goroutine运行结束的集合。你能够在主goroutine中使用Add方法添加须要等待的goroutine数量,在其它的goroutine结束的时候,须要调用Done方法来通知已经执行结束。同时,Wait方法会阻塞代码的执行直到全部的goroutine都执行完成。官方文档请点函数

这里

使用WaitGroup等待goroutine结束

在上一篇文章中,咱们使用了time.Sleep来阻塞主goroutine的执行,等待其它goroutine执行结束。如今咱们来使用WaitGroup来实现:spa

package main

import (
	"sync"
	"fmt"
)

//使用WaitGroup来等待goroutine结束
var wait sync.WaitGroup

func A(){
	fmt.Println("A执行完成。。")
	wait.Done()
}

func main(){
	//增长1,表示须要等待一个goroutine执行结束
	wait.Add(1)
	go A()
	//等待goroutine结束
	wait.Wait()
	fmt.Println("main函数执行完成")
}
//A执行完成。。
//main函数执行完成
复制代码

能够看到使用了上面的代码来等待goroutine完成更加准确,而不像time.Sleep那样可能会出现等待时间以后goroutine尚未结束或者等待时间过长的问题。code

从上面的代码,咱们能够能够看到WaitGroup实际上是使用了信号量的机制来实现的。cdn

通道

在go语言中,通道被用来在多个goroutine之间同步数据。在上一篇文章中介绍的原子操做和同步锁解决数据争用的问题。一样可使用通道来解决,并且通道更加方便,容易使用。当须要在多个goroutine中共享资源的时候,通道能够在goroutine之间传递数据,而且能够确保这个过程是同步的。而通道又分为有无缓冲通道和有缓冲通道。xml

无缓冲通道

无缓冲通道在收到数据时当即传递给另一个goroutine,不会缓冲数据。这就须要发送数据的goroutine和接收数据的goroutine同时准备好。不然就会阻塞。资源

//建立一个无缓冲通道
buffer := make(chan int)
复制代码

使用内置的make函数来建立通道,第一个参数须要使用关键字chan,以后是该通道能够传递的数据类型。上面的例子表示建立了一个能够传递int类型数据的无缓冲通道。文档

package main

import (
	"sync"
	"fmt"

)

func main(){
	//建立一个WaitGroup
	var wait sync.WaitGroup
	//信号量+2,表示等待两个goroutine完成
	wait.Add(2)

	buffer := make(chan int)
	go func(){
		fmt.Println("将数据放入通道。")
		//将12放入通道
		buffer <- 12
		//完成
		wait.Done()
	}()

	go func(){
		//从通道中获取数据
		fmt.Println("从通道拿到数据:",<- buffer)
		//关闭通道
		close(buffer)
		//完成
		wait.Done()
	}()
	//等待两个goroutine完成
	wait.Wait()
	fmt.Println("main函数执行完成。")
}
//将数据放入通道。
//从通道拿到数据: 12
//main函数执行完成。
复制代码

上面的代码,使用通道将数据从一个goroutine传递到了另一个goroutine。咱们也能够在两个goroutine之间相互传递数据。来看下面的代码:同步

package main

import (
	"sync"
	"fmt"

)

func main(){
	//建立一个WaitGroup
	var wait sync.WaitGroup
	//信号量+2,表示等待两个goroutine完成
	wait.Add(2)

	buffer := make(chan int)
	go func(name string){
		fmt.Println("将数据放入通道。")
		//将12放入通道
		buffer <- 12

		fmt.Println(name,"从通道拿到数据:",<-buffer)
		//完成
		wait.Done()
	}("A")

	go func(name string){
		//从通道中获取数据
		value := <- buffer fmt.Println(name,"从通道拿到数据:",value) //将value1 value++ //从新放入通道 buffer <- value //完成 wait.Done() }("B") //等待两个goroutine完成 wait.Wait() //关闭通道 close(buffer) fmt.Println("main函数执行完成。") } //将数据放入通道。 //B 从通道拿到数据: 12 //A 从通道拿到数据: 13 //main函数执行完成。 复制代码

从上面的代码执行结果能够看出,咱们能够在两个goroutine之间交换数据,而且通道保证了数据的同步。string

有缓冲通道

有缓冲通道表示,在通道接收到数据的时候会保留到缓冲区,等待接收goroutine的读取。只有在缓冲区满了以后,发送goroutine才会阻塞,而只有缓冲区无数据的时候,接收goroutine才会阻塞。it

//建立一个有缓冲通道,缓冲区的大小为10
buffer := make(chan int,10)
复制代码

上面的代码使用内置函数make建立了一个有缓冲的通道,缓冲的大小为10。

package main

import (
	"time"
	"sync"
	"fmt"
)

var wait sync.WaitGroup

func Producer(channel chan int){
	for i:= 1 ; i<10 ;i++{
		//休眠100毫秒
		time.Sleep(100 * time.Millisecond)

		//向通道中发送数据
		channel <- i
		fmt.Println("生产者生产:",i)
	}
	//生产完成,关闭通道
	close(channel)
	wait.Done()
}

func Consumer(channel chan int){
  //延迟执行
	defer wait.Done()
	
	for {
		//从通道中读取数据
		value ,ok := <- channel //当通道关闭的时候返回 if !ok { return } //休眠150毫秒 time.Sleep(150 * time.Millisecond) fmt.Println("消费者消费:",value) } } func main(){ //建立一个有缓冲通道,缓冲区的大小为10 buffer := make(chan int,10) wait.Add(2) go Producer(buffer) go Consumer(buffer) wait.Wait() fmt.Println("mian函数执行完成") } //生产者生产: 1 //生产者生产: 2 //消费者消费: 1 //生产者生产: 3 //消费者消费: 2 //生产者生产: 4 //生产者生产: 5 //消费者消费: 3 //生产者生产: 6 //消费者消费: 4 //生产者生产: 7 //生产者生产: 8 //消费者消费: 5 //生产者生产: 9 //消费者消费: 6 //消费者消费: 7 //消费者消费: 8 //消费者消费: 9 //mian函数执行完成 复制代码

上面的代码模拟了生产者消费者模式,生产者往通道发送数据,而消费者从通道中获取数据,当生产者生产完成以后,关闭通道。

相关文章
相关标签/搜索