zoukankan      html  css  js  c++  java
  • go的并发

    1.并发的启动,非常简单,没有什么额外操作,只需要加上go即可。

    func Afuntion(para1, para2, para3, ...) {
    	// Do some process
    	// ...
    }
    
    func main() {
    	go Afuntion(para1, para2, para3, ...) //只需加一个go前缀,Afunction()就会并发运行
    }
    

    2.go提供了sync包中的WaitGroup实现了一个类似任务队列的结构,你可以向队列中加入任务,

    任务完成后就把任务从队列中移除,如果队列中的任务没有全部完成,队列就会触发阻塞以阻止程序继续运行。

    package main
    
    import (
    	"fmt"
    	"sync"
    )
    
    var waitgroup sync.WaitGroup
    
    func Afunction(shownum int) {
    	fmt.Println(shownum)
    	waitgroup.Done() //任务完成,将任务队列中的任务数量-1,其实.Done就是.Add(-1)
    }
    
    func main() {
    	for i := 0; i < 10; i++ {
    		waitgroup.Add(1) //每创建一个goroutine,就把任务队列中任务的数量+1
    		go Afunction(i)
    	}
    	waitgroup.Wait() //.Wait()这里会发生阻塞,直到队列中所有的任务结束就会解除阻塞
    }
    

    某个地方需要创建多个goroutine,并且一定要等它们都执行完毕后再继续执行接下来的操作。

    是的,WaitGroup最大的优点就是.Wait()可以阻塞到队列中的任务都完毕后才解除阻塞。

    3.Channel,go提供的一种数据结构,先进先出。

    我们能对channel进行的操作只有4种:

    (1) 创建chennel (通过make()函数)

    (2) 放入数据 (通过 channel <- data 操作) 

    (3) 取出数据 (通过 <-channel 操作)

    (4)  关闭channel (通过close()函数)

    (1) channel是一种阻塞管道,是自动阻塞的。意思就是,如果管道满了,一个对channel放入数据的操作就会阻塞,直到有某个routine从channel中取出数据,这个放入数据的操作才会执行。相反同理,如果管道是空的,一个从channel取出数据的操作就会阻塞,直到某个routine向这个channel中放入数据,这个取出数据的操作才会执行。这事channel最重要的一个性质,没有之一。

    package main
    
    func main() {
    	ch := make(chan int, 3)
    	ch <- 1
    	ch <- 1
    	ch <- 1
    	ch <- 1 //这一行操作就会发生阻塞,因为前三行的放入数据的操作已经把channel填满了
    }
    

    (2)channel分为有缓冲的channel和无缓冲的channel。两种channel的创建方法如下:

    ch := make(chan int) //无缓冲的channel,同等于make(chan int, 0)
    ch := make(chan int, 5) //一个缓冲区大小为5的channel
    

    (3)关闭后的channel可以取数据,但是不能放数据。而且,channel在执行了close()后并没有真的关闭,channel中的数据全部取走之后才会真正关闭。

    package main
    
    func main() {
    	ch := make(chan int, 5)
    	ch <- 1
    	ch <- 1
    	close(ch)
    	ch <- 1 //不能对关闭的channel执行放入操作
            
            // 会触发panic
    }
    

    3.channel相比WaitGroup有一个很大的优点,就是channel不仅可以实现协程的同步,而且可以控制当前正在运行的goroutine的总数。

    一.如果任务数量是固定的:
    
    
    package main
    
    func Afunction(ch chan int) {
    	ch <- 1
    }
    
    func main() {
    	var (
    		ch        chan int = make(chan int, 20) //可以同时运行的routine数量为20
    		dutycount int      = 500
    	)
    	for i := 0; i < dutycount; i++ {
    		go Afunction(ch)
    	}
    
    	//知道了任务总量,可以像这样利用固定循环次数的循环检测所有的routine是否工作完毕
    	for i := 0; i < dutycount; i++ {
    		<-ch
    	}
    }
    

      

    二.如果任务的数量不固定
    package main
    
    import (
    	"fmt"
    )
    
    func Afunction(routineControl chan int, feedback chan string) {
    	defer func() {
    		<-routineControl
    		feedback <- "finish"
    	}()
    
    	// do some process
    	// ...
    }
    
    func main() {
    	var (
    		routineCtl chan int    = make(chan int, 20)
    		feedback   chan string = make(chan string, 10000)
    
    		msg      string
    		allwork  int
    		finished int
    	)
    	for i := 0; i < 1000; i++ {
    		routineCtl <- 1
    		allwork++
    		go Afunction(routineCtl, feedback)
    	}
    
    	for {
    		msg = <-feedback
    		if msg == "finish" {
    			finished++
    		}
    		if finished == allwork {
    			break
    		}
    	}
    }
    

      

  • 相关阅读:
    第26月第26天 Domain=AVFoundationErrorDomain Code=-11850
    第26月第25天 ubuntu openjdk-8-jdk jretty
    第26月第23天 nsobject 单例 CFAbsoluteTimeGetCurrent
    第26月第22天 iOS瘦身之armv7 armv7s arm64选用 iOS crash
    第26月第20天 springboot
    第26月第18天 mybatis_spring_mvc pom
    python中的字符数字之间的转换函数
    python if else elif statement
    python 赋值魔法
    python print import使用
  • 原文地址:https://www.cnblogs.com/huangxiaohen/p/4304914.html
Copyright © 2011-2022 走看看