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死循环
		}
	}
}

原文地址:https://www.cnblogs.com/suolu/p/6718807.html