Go并发原语/并发组件/go并发核心语法 之goroutine

goroutine:
goroutine 是一个更高级别的抽象,称为协程。协程是非抢占式的。
Go语言的运行时会观察gorountine的运行时行为,并在它们阻塞时自动挂起它们,然后在他们不被阻塞时恢复它们。在某种程度上,这使它们成为可抢占的(只是
在gorountine 被阻塞的情况)

Go语言遵循一个称为fork-join的并发模型
func main()  {
	sayHello := func(){
		fmt.Println("hello")
	}
	go sayHello()
	//继续执行自己的逻辑
}
//上个例子, sayHello函数将在gorountine 上运行,而程序的其余部分将继续执行。在本例中没有join点。

//也许你会说 我在 go sayHello() 之后加上 time.Sleep 。但这种方式没有加入join点,只是增加了一个竞争条件。这只是增加了goroutine在程序退出前执行的概率,但并不能保证一定能执行。

join 点:

为了创建一个join点,可以通过多种方式实现; 其一: 使用sync 包中 sync.Waitgroup

func main()  {
	var wg sync.WaitGroup
	sayHello := func() {
		defer wg.Done()
		fmt.Println("hello")
	}

	wg.Add(1)
	go sayHello()
	wg.Wait()   // 一 :join点
}

gorountine 闭包:

如果gorountine 内部引用 外部变量,则这是原值的引用
 func main(){
 	var wg sync.WaitGroup
 	for _, salutation := range []string{"hello", "greetings", "good day"}{
 		wg.Add(1)
 		go func() {
 			defer wg.Done()
 			fmt.Println(salutation)
		}()
	}

 	wg.Wait()
 	//fmt.Println(salutation)  //这行会报错,正常情况下 for  range 结束后 salutation 就不能访问了,超出作用域了
 }

//good day
//good day
//good day

程序说明:

循环有很高的概率 在 gorountine 执行前 退出
正常情况下 for  range 结束后 salutation 就不能访问了,超出作用域了; gorountine怎么能访问salutation呢?
对变量salutation值的引用仍然保留,由内存转移到堆,以便gorountine可以继续访问它。
如果想看到期望的打印结果,应该把salutation的副本传递到闭包中
  func main(){
  	var wg sync.WaitGroup
  	for _, salutation := range []string{"hello", "greetings", "good day"}{
  		wg.Add(1)
  		go func(salutation string) {
  			defer wg.Done()
  			fmt.Println(salutation)
		}(salutation)
	}

  	wg.Wait()
  }

//good day
//hello
//greetings

goroutine占用内存  /goroutine非常轻:

一个新创建的gorountine 只需要几千字节。当它不运行时,Go语言运行时就会自动增长(缩小)堆栈的内存
func main(){
 	memConsumed := func() uint64{
 		runtime.GC()
 		var s runtime.MemStats
 		runtime.ReadMemStats(&s)
 		return s.Sys
	}

 	var c <-chan interface{}
 	var wg sync.WaitGroup
 	noop := func() {
 		wg.Done();
 		<-c    //防止 goroutine 退出
	}

 	const numGoroutines = 1e4
 	wg.Add(numGoroutines)
 	before := memConsumed()
 	for i := numGoroutines; i>0; i--{
 		go noop()
	}

	wg.Wait()
 	after := memConsumed()
 	fmt.Printf("%.3fkb", float64(after - before)/numGoroutines/1000)   //0.177kb
 }
//大致的数据: 8G内存 可以启动 2百多万 goroutine

goroutine切换上下文需要的时间:/0.268μs

func BenchmarkContextSwitch(b *testing.B){
	var wg sync.WaitGroup
	begin := make(chan struct{})
	c := make(chan struct{})

	var token struct{}
	sender := func() {
		defer wg.Done()
		<-begin         //在这里等待,直到被告知开始执行
		for i:=0; i < b.N; i++{
			c <- token   //一个struct{} 被称为一个空结构,它没有内存占用。
		}
	}

	receiver := func() {
		defer wg.Done()
		<- begin	////在这里等待,直到被告知开始执行
		for i := 0; i< b.N; i++{
			<- c
		}
	}

	wg.Add(2)
	go sender()
	go receiver()
	b.StartTimer()   //开始计时
	close(begin)     //发送开始信号
	wg.Wait()		//告诉两个goroutine 开始运行

}
//268 ns/op
在操作系统级别,使用线程可能非常昂贵。大致数据 1.467μs ; gorountine切换只需要 0.268μs;

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值