go语言之并发

前言

之前我博客里介绍的都是基础编程,从今天开始才算正式的接触真正的go语言了,这也是go语言的魅力。并发在编程中是一个很重要的概念,go语言天生支持并发。

定义

程序可以在不同的处理器和计算机上同时执行不同的代码段。Go 语言为构建并发程序的基本代码块是协程 (goroutine) 与通道 (channel)。他们需要语言,编译器,和runtime的支持。Go 语言提供的垃圾回收器对并发编程至关重要。

并发:同一时间段内执行多个任务,比如13:00—15:00,我在和我的女朋用微信聊天,并且我也在打游戏。

并行:同一时刻执行多个任务,比如13:00我在用微信和女朋友聊天,我女朋友的妈妈也在用微信和他聊天。

并行是一种通过使用多处理器以提高速度的能力。所以并发程序可以是并行的,也可以不是。

Go语言的并发是通过goroutine实现的。goroutine是由go语言在运行时调度完成,而线程是由操作系统调度完成。

goroutine

Go语言中的goroutine就是这样一种机制,goroutine的概念类似于线程,但 goroutine是由Go的运行时(runtime)调度和管理的。Go程序会智能地将 goroutine 中的任务合理地分配给每个CPU。Go语言之所以被称为现代化的编程语言,就是因为它在语言层面已经内置了调度和上下文切换的机制

使用goroutine

Go语言使用goroutine很简便,在调用函数的时候在前面加go关键字,就可以为一个函数创建一个goroutine。

一个goroutine必定对应一个函数,可以创建多个goroutine去执行相同的函数

启动单个goroutine

代码实现

package main

import (
	"fmt"
	"time"
)

//并发:同一时间段内执行多个任务
//并行:同一时刻执行多个任务
func hello(i int) {
	fmt.Println("hello", i)
}

//程序启动之后会创建一个主goroutine去执行
func main() {
	for i := 0; i < 10; i++ {
		go hello(i) //开启一个单独的goroutine去执行hello()函数
		// go func(i int) {
		// 	fmt.Println(i)
		// }(i)
	}
	fmt.Println("main")
	time.Sleep(time.Second)
}

启动多个goroutine

package main

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

var wg sync.WaitGroup

func f1(i int) {
	defer wg.Done()
	time.Sleep(time.Microsecond * time.Duration(rand.Intn(300)))
	fmt.Println(i)
}
func main() {
	// wg.Add(10)
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go f1(i)
	}
	wg.Wait()
}

groutine与线程

1、可增长的栈

OS线程(操作系统线程)一般都有固定的栈内存(通常为2MB),一个gotoutine的栈在其生命周期开始时只有2kb,但是goroutine的栈是可以变化的。

2、goroutine调度

GPM:是Go语言运行时(runtime)层面的实现,是go语言自己实现的一套调度系统;区别于操作系统调度os线程

G:存放本goroutine和所在P的绑定等信息

P:管理着一组goroutine队列,P里面会存储当前goroutine运行的上下文环境(函数指针,堆栈地址及地址边界),P会对自己管理的goroutine队列做一些调度(比如把占用CPU时间较长的goroutine暂停、运行后续的goroutine等等)当自己的队列消费完了就去全局队列里取,如果全局队列里也消费完了会去其他P的队列里抢任务。

M:是Go运行时对操作系统内核线程的虚拟,M与内核贤臣跟一般是一一映射关系,一个goroutine最终是要放到M上执行的;

P与M一般也是一一对应的。他们关系是: P管理着一组G挂载在M上运行。当一个G长久阻塞在一个M上时,runtime会新建一个M,阻塞G所在的P会把其他的G 挂载在新建的M上。当旧的G阻塞完成或者认为其已经死掉时 回收旧的M。

单从线程调度讲,Go语言相比起其他语言的优势在于OS线程是由OS内核来调度的,goroutine则是由Go运行时(runtime)自己的调度器调度的,这个调度器使用一个称为m:n调度的技术(复用/调度m个goroutine到n个OS线程)。

GOMAXPROCS

package main

import (
	"fmt"
	"runtime"
	"sync"
)

var wg sync.WaitGroup

func a() {
	defer wg.Done()
	for i := 0; i < 10; i++ {
		fmt.Printf("A:%d\n", i)
	}
}
func b() {
	defer wg.Done()
	for i := 0; i < 10; i++ {
		fmt.Printf("B:%d\n", i)
	}
}
func main() {
	runtime.GOMAXPROCS(1) //默认就是cpu逻辑核心数,默认跑满整个cpu
	fmt.Println(runtime.NumCPU())
	wg.Add(2)
	go a()
	go b()
	wg.Wait()
}

Go语言中的操作系统线程和goroutine的关系:

  1. 一个操作系统线程对应用户态多个goroutine。
  2. go程序可以同时使用多个操作系统线程。
  3. goroutine和OS线程是多对多的关系,即m:n。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值