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死循环
    		}
    	}
    }
    
    
  • 相关阅读:
    筛选法 || POJ 1356 Prime Land
    搜索 || BFS || POJ 3278 Catch That Cow
    (素数筛) 找质数
    (map)后缀字符串
    字符串的进制
    (二进制枚举子集)买玩具
    (基础)01背包问题
    (基础)编辑距离
    (基础)最长公共字串
    最大子矩阵和
  • 原文地址:https://www.cnblogs.com/suolu/p/6718807.html
Copyright © 2011-2022 走看看