首页 > 其他分享 >go语言调度gmp原理(5)

go语言调度gmp原理(5)

时间:2023-05-23 23:56:31浏览次数:38  
标签:runtime thread gp 调度 ts 线程 mp go gmp

go语言调度gmp原理(5)

线程管理

go语言的运行时会通过调度器改变线程的所有权,它也提供了runtime.lockOSthread和runtime.UnlockOSthread,让我们能绑定goroutine和线程完成一些比较特殊的操作。goroutine应该在调用操作系统服务或者依赖线程状态的非go语言库时调用runtime.lockOSThread函数,例如C语言图形库等

runtime.lockOSThread会通过如下所示的代码绑定goroutine和当前线程

func lockOSThread() {
	getg().m.lockedInt++
	dolockOSThread()
}

func dolockOSThread() {
	if GOARCH == "wasm" {
		return // no threads on wasm yet
	}
	gp := getg()
	gp.m.lockedg.set(gp)
	gp.lockedm.set(gp.m)
}

runtime.dolockOSThread会分别设置线程的lockedg字段和goroutine的lockedm字段,这两行代码会绑定线程和goroutine

当goroutine完成特定操作之后,会调用函数runtime.UnlockOSThread分离goroutine和线程

func unlockOSThread() {
	gp := getg()
	if gp.m.lockedInt == 0 {
		systemstack(badunlockosthread)
	}
	gp.m.lockedInt--
	dounlockOSThread()
}

func dounlockOSThread() {
	if GOARCH == "wasm" {
		return // no threads on wasm yet
	}
	gp := getg()
	if gp.m.lockedInt != 0 || gp.m.lockedExt != 0 {
		return
	}
	gp.m.lockedg = 0
	gp.lockedm = 0
}

函数执行的过程与runtime.LockOSThread正好相反。在多数服务之中,我们用不到这对函数

线程的生命周期

go语言的运行时会通过runtime.startm启动线程来执行处理器P,如果我们在该函数中没能从空闲列表中获取线程M,就会调用runtime.newm创建新线程

func newm(fn func(), pp *p, id int64) {
	// allocm adds a new M to allm, but they do not start until created by
	// the OS in newm1 or the template thread.
	//
	// doAllThreadsSyscall requires that every M in allm will eventually
	// start and be signal-able, even with a STW.
	//
	// Disable preemption here until we start the thread to ensure that
	// newm is not preempted between allocm and starting the new thread,
	// ensuring that anything added to allm is guaranteed to eventually
	// start.
	acquirem()

	mp := allocm(pp, fn, id)
	mp.nextp.set(pp)
	mp.sigmask = initSigmask
	if gp := getg(); gp != nil && gp.m != nil && (gp.m.lockedExt != 0 || gp.m.incgo) && GOOS != "plan9" {
		// We're on a locked M or a thread that may have been
		// started by C. The kernel state of this thread may
		// be strange (the user may have locked it for that
		// purpose). We don't want to clone that into another
		// thread. Instead, ask a known-good thread to create
		// the thread for us.
		//
		// This is disabled on Plan 9. See golang.org/issue/22227.
		//
		// TODO: This may be unnecessary on Windows, which
		// doesn't model thread creation off fork.
		lock(&newmHandoff.lock)
		if newmHandoff.haveTemplateThread == 0 {
			throw("on a locked thread with no template thread")
		}
		mp.schedlink = newmHandoff.newm
		newmHandoff.newm.set(mp)
		if newmHandoff.waiting {
			newmHandoff.waiting = false
			notewakeup(&newmHandoff.wake)
		}
		unlock(&newmHandoff.lock)
		// The M has not started yet, but the template thread does not
		// participate in STW, so it will always process queued Ms and
		// it is safe to releasem.
		releasem(getg().m)
		return
	}
	newm1(mp)
	releasem(getg().m)
}

func newm1(mp *m) {
	if iscgo {
		var ts cgothreadstart
		if _cgo_thread_start == nil {
			throw("_cgo_thread_start missing")
		}
		ts.g.set(mp.g0)
		ts.tls = (*uint64)(unsafe.Pointer(&mp.tls[0]))
		ts.fn = unsafe.Pointer(abi.FuncPCABI0(mstart))
		if msanenabled {
			msanwrite(unsafe.Pointer(&ts), unsafe.Sizeof(ts))
		}
		if asanenabled {
			asanwrite(unsafe.Pointer(&ts), unsafe.Sizeof(ts))
		}
		execLock.rlock() // Prevent process clone.
		asmcgocall(_cgo_thread_start, unsafe.Pointer(&ts))
		execLock.runlock()
		return
	}
	execLock.rlock() // Prevent process clone.
	newosproc(mp)
	execLock.runlock()
}

创建新线程需要使用如下所示的runtime.newosproc,该函数在Linux平台上会通过系统调用clone创建新的操作系统线程,它也是创建线程链路上距离操作系统最近的go语言函数

func newosproc(mp *m) {
	// We pass 0 for the stack size to use the default for this binary.
	thandle := stdcall6(_CreateThread, 0, 0,
		abi.FuncPCABI0(tstart_stdcall), uintptr(unsafe.Pointer(mp)),
		0, 0)

	if thandle == 0 {
		if atomic.Load(&exiting) != 0 {
			// CreateThread may fail if called
			// concurrently with ExitProcess. If this
			// happens, just freeze this thread and let
			// the process exit. See issue #18253.
			lock(&deadlock)
			lock(&deadlock)
		}
		print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", getlasterror(), ")\n")
		throw("runtime.newosproc")
	}

	// Close thandle to avoid leaking the thread object if it exits.
	stdcall1(_CloseHandle, thandle)
}

使用系统调用clone创建的线程会在线程主动调用exit或者传入的函数runtime.mstart返回时主动退出,runtime.mstart会执行调用runtime.newm时传入的匿名函数fn,至此也就完成了从线程创建到销毁的整个闭环

小结

调度器主要函数运行逻辑

img

标签:runtime,thread,gp,调度,ts,线程,mp,go,gmp
From: https://www.cnblogs.com/zpf253/p/17426798.html

相关文章

  • go检查所有主机的时间同步
    funccheckTimeSync(sssh.Interface,ipList[]string)error{logger.Info("checker:timeSync%v",ipList)for_,ip:=rangeipList{timeStamp,err:=s.CmdToString(ip,"date+%s","")iferr!=nil{ret......
  • go检测状态 template
    typesystemStatusstruct{NamestringStatusstring}typeInitSystemStatusstruct{ErrorstringServiceList[]systemStatus}func(n*InitSystemChecker)Output(status*InitSystemStatus)error{tpl,isOk,err:=template.TryParse(`System......
  • Flask与Django项目运行
    Django要运行Django项目,你可以按照以下步骤进行操作:1.确保你已经安装了Python和Django。如果你还没有安装它们,请先安装它们。2.打开终端或命令提示符。3.导航到你的Django项目的根目录。这是包含manage.py文件的目录。4.运行以下命令以应用数据库迁移:pythonmanage.pymigra......
  • Golang - 获取指定区间范围内随机数
    1.go语言未提供获取指定区间范围内的随机数方法,只有一个参数获取(0,n)的随机数。2.可根据此方法获取指定区间随机数,先随机0到(m-n),再用加 n的方式解决例:[5,10],先生成[0,5],再加5packagemainimport("fmt""math/rand""time")funcmain(){//......
  • 解决nginx+django+swagger打开swagger需要进行Django Login
    一、将drf-yasg包的static目录拷贝到Django项目根目录下二、修改nginx配置,添加如下红框中的内容三、重启nginxsystemctlrestartnginx这样再次打开http://XXX:XX/swagger/即可直接打开swagger接口文档 ......
  • MongoDB入门
    一、业务应用场景三高需求:高并发读写需求海量数据的高效率存储和访问的需求对数据库的高扩展性和高可用性的需求应用场景:社交场景,使用MongoDB存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人,地点等功能游戏场景:方便查询、高效率存储和访问物流场景:......
  • mysql、redis、mongo本地docker部署命令备忘
    1mysqldocker环境部署####获取镜像dockerpullredis####启动mysqldockerrun--name=mysql-it-p3306:3306-eMYSQL_ROOT_PASSWORD=123456-dmysql####登录mysql-h127.0.0.1-P3306-uroot-p1234562redisdocker环境部署####官⽅方指引https://hub.docker.c......
  • Maven报错 解决方案。ERROR: No goals have been specified for this build. You must
    转:https://www.codeleading.com/article/61821466327/报错:[ERROR]Nogoalshavebeenspecifiedforthisbuild.Youmustspecifyavalidlifecyclephaseoragoalintheformat<plugin-prefix>:<goal>or<plugin-group-id>:<plugin-artifact-......
  • Algorithm_01--C#递归算法
    ///递归算法本质:///1、方法的自我调用///2、有明确的终止条件///3、每次调用时,问题规模在不断减少。通过递减,最终到达终止条件  问题:程序在输入1000后(即1到1000的和),程序会出现异常。解答:百度后得出结论,栈溢出异常。1、递归......
  • django——继承默认User模型进行自定义
    自定义用户模型在Django中非常常见。下面是一个简单的示例,演示如何扩展Django默认的User模型,以添加自定义字段和方法:python复制代码fromdjango.contrib.auth.modelsimportAbstractUserfromdjango.dbimportmodelsclassCustomUser(AbstractUser):#添加自定......