zoukankan      html  css  js  c++  java
  • GO入门——7. 并发

    1 并发

    1.1 goroutine

    Goroutine 奉行通过通信来共享内存,而不是共享内存来通信

    • goroutine 只是由官方实现的超级“线程池”而已。
    • 每个实例 4-5KB 的栈内存占用和由于实现机制而大幅
      减少的创建和销毁开销,是制造 Go 号称的高并发的根本原因。
    • goroutine 的简单易用,也在语言层面上给予了开发者巨大的便利

    1.2 Channel

    • Channel 是 goroutine -沟通的桥梁,大都是阻塞同步的
    • 通过 make 创建,close 关闭
    • Channel 是引用类型
    • 可以使用 for range 来迭代不断操作 channel
    • 可以设置单向或双向通道
    • 可以设置缓存大小,在未被填满前不会发生阻塞

    1.3 Select

    • 可处理一个或多个 channel 的发送与接收
    • 同时有多个可用的 channel时按随机顺序处理
    • 可用空的 select 来阻塞 main 函数
    • 可设置超时
    package main
    
    import (
    	"fmt"
    	"runtime"
    	"sync"
    	"time"
    )
    
    func main() {
    	testSelectTimeout()
    }
    
    func testMain() {
    	//当只执行这一句时没有结果输出
    	//原因在于go将函数放入其他线程执行,当main结束后其他线程也将结束
    	go func() {
    		fmt.Println("Go GO GO !!!")
    	}()
    }
    
    //channel是引用类型,所以不需要传指针类型即可修改
    func goFunc(c chan bool) {
    	fmt.Println("GO GO GO")
    	c <- true
    }
    
    //channel
    func testChannel() {
    	//make(chan bool,x),设置缓存为x
    	//当有缓存时,如果缓存还没满则向channel插入值是不会阻塞的
    	//创建无缓存的channel
    	c := make(chan bool)
    	go goFunc(c)
    	//此时从channel中取值,如果channel中没有值则将阻塞
    	//因此必然将先执行完goFunc然后main才结束
    	<-c
    }
    
    //channel配合range
    func testChannelRange() {
    	c := make(chan bool)
    	go func() {
    		fmt.Println("GO GO GO")
    		c <- true
    		close(c) //关闭channel,如果不关闭range将一直阻塞main线程
    	}()
    	for v := range c { //从channel中一直取值,直到channel被关闭
    		fmt.Println(v) //输出true
    	}
    }
    
    //
    func testCurrentByChannel() {
    	//设置GO使用cup核数,好像默认就是多核
    	runtime.GOMAXPROCS(runtime.NumCPU())
    	c := make(chan bool, 10)
    	f := func(index int) {
    		sum := 0
    		for i := 0; i < 1000000; i++ {
    			sum += i
    		}
    		fmt.Println(index, sum)
    		c <- true
    	}
    	for i := 0; i < 10; i++ {
    		go f(i)
    	}
    	for i := 0; i < 10; i++ {
    		<-c
    	}
    }
    
    //WaitGroup类似java中的CountDownLatch
    func testCurrentByWatiGroup() {
    	//设置GO使用cup核数,好像默认就是多核
    	runtime.GOMAXPROCS(runtime.NumCPU())
    	wg := sync.WaitGroup{}
    	wg.Add(10)
    	//需要传入指针
    	f := func(index int, wg *sync.WaitGroup) {
    		sum := 0
    		for i := 0; i < 1000000; i++ {
    			sum += i
    		}
    		fmt.Println(index, sum)
    		wg.Done()
    	}
    	for i := 0; i < 10; i++ {
    		go f(i, &wg)
    	}
    	wg.Wait()
    }
    
    //有问题。。。。
    //当channel都无缓存时关闭其中一个channel,再向另一个写值,此时将发生死锁
    //当channel有缓存,关闭的那个channel将一直输出(空,false)
    func testSelect() {
    	//select 无法保证多个channel都关闭
    	c1, c2 := make(chan int), make(chan bool)
    	o := make(chan bool)
    	go func() {
    		//f1, f2 := false, false
    		for {
    			select {
    			//当某个channel关闭后,其case将一直执行,返回一个(v=空值,ok=false)
    			//因此此时select将
    			case v, ok := <-c1:
    				{
    					fmt.Println("oc1")
    					if !ok {
    						fmt.Println("c1")
    						o <- true
    						break
    					}
    					fmt.Println("c1", v)
    				}
    			case v, ok := <-c2:
    				{
    					if !ok {
    						o <- true
    						break
    					}
    					fmt.Println("c2", v)
    				}
    			}
    		}
    	}()
    	c1 <- 0
    	c1 <- 1
    	c2 <- true
    	c1 <- 2
    	c2 <- false
    	c1 <- 3
    
    	close(c1)
    	//close(c2)
    	c2 <- false
    	//c2 <- false
    	for i := 0; i < 2; i++ {
    		<-o
    		fmt.Println("o")
    	}
    
    }
    
    //select作为发送者
    func testSelectSend() {
    	c := make(chan int)
    
    	go func() {
    		for i := 0; i < 10; i++ {
    			//随机向c中写入0或1
    			select {
    			case c <- 0:
    			case c <- 1:
    			}
    		}
    	}()
    	for i := 0; i < 10; i++ {
    		fmt.Println(<-c)
    	}
    	select {} //阻塞程序
    }
    
    //select timeout
    func testSelectTimeout() {
    	c := make(chan int)
    	go func() {
    		for i := 0; i < 5; i++ {
    			time.Sleep(1 * time.Second)
    			c <- i
    		}
    
    	}()
    Label:
    	for {//不加for则只会输出一次case的匹配就结束了
    		select {
    		case v := <-c:
    			fmt.Println(v)
    		case <-time.After(3 * time.Second):	//3s没获取到值时将执行
    			fmt.Println("Timeout") //输出
    			break Label	//退出select死循环
    		}
    	}
    }
    
    
  • 相关阅读:
    cart树剪枝
    LSA、LDA
    paddle中新增layer
    https://www.i5seo.com/
    打印机彩色打印设置(将彩色打印为黑色)
    办公文档的标准格式
    电脑常用的5个按键
    Win7各个版本之间的区别
    win7保护眼睛的颜色设置方法(85,125,205)
    详细教您台式电脑如何组装
  • 原文地址:https://www.cnblogs.com/suolu/p/6718807.html
Copyright © 2011-2022 走看看