最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go?runtime?調(diào)度器之系統(tǒng)調(diào)用引起的搶占

 更新時間:2024年09月17日 10:06:40   作者:胡云Troy  
本文解析了在Go語言中,當goroutine執(zhí)行的系統(tǒng)調(diào)用時間過長時,系統(tǒng)如何通過監(jiān)控和搶占機制來處理,以維持運行效率和資源分配的平衡,通過具體的示例和流程圖,詳細展示了系統(tǒng)調(diào)用過程中的搶占操作,感興趣的朋友跟隨小編一起看看吧

0. 前言

第八講介紹了當 goroutine 運行時間過長會被搶占的情況。這一講繼續(xù)看 goroutine 執(zhí)行系統(tǒng)調(diào)用時間過長的搶占。

1. 系統(tǒng)調(diào)用時間過長的搶占

看下面的示例:

func longSyscall() {
	timeout := syscall.NsecToTimeval(int64(5 * time.Second))
	fds := make([]syscall.FdSet, 1)
	if _, err := syscall.Select(0, &fds[0], nil, nil, &timeout); err != nil {
		fmt.Println("Error:", err)
	}
	fmt.Println("Select returned after timeout")
}
func main() {
	threads := runtime.GOMAXPROCS(0)
	for i := 0; i < threads; i++ {
		go longSyscall()
	}
	time.Sleep(8 * time.Second)
}

longSyscall goroutine 執(zhí)行一個 5s 的系統(tǒng)調(diào)用,在系統(tǒng)調(diào)用過程中,sysmon 會監(jiān)控 longSyscall,發(fā)現(xiàn)執(zhí)行系統(tǒng)調(diào)用過長,會對其搶占。

回到 sysmon 線程看它是怎么搶占系統(tǒng)調(diào)用時間過長的 goroutine 的。

func sysmon() {
    ...
    idle := 0 // how many cycles in succession we had not wokeup somebody
    delay := uint32(0)
    ...
    for {
		if idle == 0 { // start with 20us sleep...
			delay = 20
		} else if idle > 50 { // start doubling the sleep after 1ms...
			delay *= 2
		}
		if delay > 10*1000 { // up to 10ms
			delay = 10 * 1000
		}
		usleep(delay)
        ...
        // retake P's blocked in syscalls
		// and preempt long running G's
		if retake(now) != 0 {
			idle = 0
		} else {
			idle++
		}
        ...
    }
}

類似于運行時間過長的 goroutine,調(diào)用 retake 進行搶占:

func retake(now int64) uint32 {
	n := 0
	lock(&allpLock)
	for i := 0; i < len(allp); i++ {
		pp := allp[i]
		if pp == nil {
			continue
		}
		pd := &pp.sysmontick
		s := pp.status
		sysretake := false
        if s == _Prunning || s == _Psyscall {                           // goroutine 處于 _Prunning 或 _Psyscall 時會搶占
			// Preempt G if it's running for too long.
			t := int64(pp.schedtick)
			if int64(pd.schedtick) != t {
				pd.schedtick = uint32(t)
				pd.schedwhen = now
			} else if pd.schedwhen+forcePreemptNS <= now {   
                // 對于 _Prunning 或者 _Psyscall 運行時間過長的情況,都會進入 preemptone
                // preemptone 我們在運行時間過長的搶占中介紹過,它主要設(shè)置了 goroutine 的標志位
                // 對于處于系統(tǒng)調(diào)用的 goroutine,這么設(shè)置并不會搶占。因為線程一直處于系統(tǒng)調(diào)用狀態(tài)           
				preemptone(pp)                                          
				// In case of syscall, preemptone() doesn't
				// work, because there is no M wired to P.
				sysretake = true
			}
		}
        if s == _Psyscall {                                             
            // Retake P from syscall if it's there for more than 1 sysmon tick (at least 20us).
            // P 處于系統(tǒng)調(diào)用之中,需要檢查是否需要搶占
            // syscalltick 用于記錄系統(tǒng)調(diào)用的次數(shù),在完成系統(tǒng)調(diào)用之后加 1
			t := int64(pp.syscalltick)
			if !sysretake && int64(pd.syscalltick) != t {
                // pd.syscalltick != pp.syscalltick,說明已經(jīng)不是上次觀察到的系統(tǒng)調(diào)用了,  
                // 而是另外一次系統(tǒng)調(diào)用,需要重新記錄 tick 和 when 值
				pd.syscalltick = uint32(t)
				pd.syscallwhen = now
				continue
			}
            // On the one hand we don't want to retake Ps if there is no other work to do,
			// but on the other hand we want to retake them eventually
			// because they can prevent the sysmon thread from deep sleep.
            // 如果滿足下面三個條件的一個則執(zhí)行搶占:
            // 1. 線程綁定的本地隊列中有可運行的 goroutine
            // 2. 沒有無所事事的 P(表示大家都挺忙的,那就不要執(zhí)行系統(tǒng)調(diào)用那么長時間占資源了)
            // 3. 執(zhí)行系統(tǒng)調(diào)用時間超過 10ms 的
			if runqempty(pp) && sched.nmspinning.Load()+sched.npidle.Load() > 0 && pd.syscallwhen+10*1000*1000 > now {
				continue
			}
            // 下面是執(zhí)行搶占的邏輯
            unlock(&allpLock)
			// Need to decrement number of idle locked M's
			// (pretending that one more is running) before the CAS.
			// Otherwise the M from which we retake can exit the syscall,
			// increment nmidle and report deadlock.
			incidlelocked(-1)
			if atomic.Cas(&pp.status, s, _Pidle) {                  // 將 P 的狀態(tài)更新為 _Pidle
				n++                                                 // 搶占次數(shù) + 1
				pp.syscalltick++                                    // 系統(tǒng)調(diào)用搶占次數(shù) + 1              
				handoffp(pp)                                        // handoffp 搶占
			}
			incidlelocked(1)
			lock(&allpLock)
        }
    }
    unlock(&allpLock)
	return uint32(n)
}

進入 handoffp

// Hands off P from syscall or locked M.
// Always runs without a P, so write barriers are not allowed.
//
//go:nowritebarrierrec
func handoffp(pp *p) {
    // if it has local work, start it straight away
    // 這里如果 P 的本地有工作(goroutine),或者全局有工作的話
    // 將 P 和其它線程綁定,其它線程指的是不是執(zhí)行系統(tǒng)調(diào)用的那個線程
    // 執(zhí)行系統(tǒng)調(diào)用的線程不需要 P 了,這時候把 P 釋放出來,算是資源的合理利用,相比于線程,P 是有限的
	if !runqempty(pp) || sched.runqsize != 0 {
		startm(pp, false, false)
		return
	}
    ...
    // no local work, check that there are no spinning/idle M's,
	// otherwise our help is not required
	if sched.nmspinning.Load()+sched.npidle.Load() == 0 && sched.nmspinning.CompareAndSwap(0, 1) { // TODO: fast atomic
		sched.needspinning.Store(0)
		startm(pp, true, false)
		return
	}
    ...
    // 判斷全局隊列有沒有工作要處理
    if sched.runqsize != 0 {
		unlock(&sched.lock)
		startm(pp, false, false)
		return
	}
    ...
    // 如果都沒有工作,那就把 P 放到全局空閑隊列中
    pidleput(pp, 0)
	unlock(&sched.lock)
}

可以看到搶占系統(tǒng)調(diào)用過長的 goroutine,這里搶占的意思是釋放系統(tǒng)調(diào)用線程所綁定的 P,搶占的意思不是不讓線程做系統(tǒng)調(diào)用,而是把 P 釋放出來。(由于前面設(shè)置了這個 goroutine 的 stackguard0,類似于 運行時間過長 goroutine 的搶占 的流程還是會走一遍的)。

我們看一個示意圖可以更直觀清晰的了解這個過程:

handoff 結(jié)束之后,增加搶占次數(shù) n,retake 返回:

func sysmon() {
	...
	idle := 0 // how many cycles in succession we had not wokeup somebody
	delay := uint32(0)
	for {
		if idle == 0 { // start with 20us sleep...
			delay = 20                  // 如果 idle == 0,表示 sysmon 需要打起精神來,要隔 20us 監(jiān)控一次
		} else if idle > 50 { // start doubling the sleep after 1ms...
			delay *= 2                  // 如果 idle 大于 50,表示循環(huán)了 50 次都沒有搶占,sysmon 將加倍休眠,比較空,sysmon 也不浪費資源,先睡一會
		}
		if delay > 10*1000 { // up to 10ms
			delay = 10 * 1000           // 當然,不能無限制睡下去。最大休眠時間設(shè)置成 10ms
		}
        if retake(now) != 0 {               
			idle = 0                    // 有搶占,則 idle = 0,表示 sysmon 要忙起來
		} else {
			idle++                      // 沒有搶占,idle + 1
		}
    ...
    }
    ...
}

2. 小結(jié)

本講介紹了系統(tǒng)調(diào)用時間過長引起的搶占。下一講將繼續(xù)介紹異步搶占。

到此這篇關(guān)于Go runtime 調(diào)度器之系統(tǒng)調(diào)用引起的搶占的文章就介紹到這了,更多相關(guān)Go runtime 調(diào)度器 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • go?singleflight緩存雪崩源碼分析與應用

    go?singleflight緩存雪崩源碼分析與應用

    這篇文章主要為大家介紹了go?singleflight緩存雪崩源碼分析與應用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-09-09
  • Go設(shè)計模式之觀察者模式講解和代碼示例

    Go設(shè)計模式之觀察者模式講解和代碼示例

    觀察者是一種行為設(shè)計模式, 允許一個對象將其狀態(tài)的改變通知其他對象,觀察者模式提供了一種作用于任何實現(xiàn)了訂閱者接口的對象的機制, 可對其事件進行訂閱和取消訂閱,本文就通過代碼示例給大家詳細介紹一下Go的觀察者模式,需要的朋友可以參考下
    2023-07-07
  • GoLang橋接模式的實現(xiàn)示例

    GoLang橋接模式的實現(xiàn)示例

    橋接模式是一種結(jié)構(gòu)型設(shè)計模式,通過橋接模式可以將抽象部分和它的實現(xiàn)部分分離,本文主要介紹了GoLang橋接模式,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • golang小游戲開發(fā)實戰(zhàn)之飛翔的小鳥

    golang小游戲開發(fā)實戰(zhàn)之飛翔的小鳥

    這篇文章主要給大家介紹了關(guān)于golang小游戲開發(fā)實戰(zhàn)之飛翔的小鳥的相關(guān)資料,,本文可以帶你你從零開始,一步一步的開發(fā)出這款小游戲,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-03-03
  • go自動下載所有的依賴包go module使用詳解

    go自動下載所有的依賴包go module使用詳解

    這篇文章主要介紹了go自動下載所有的依賴包go module使用詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-03-03
  • golang Iris運行多個應用的實現(xiàn)

    golang Iris運行多個應用的實現(xiàn)

    本文主要介紹了golang Iris運行多個應用的實現(xiàn),在Iris里面,提供了一種方式可以讓我們同時運行多個應用,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • go語言實現(xiàn)一個簡單的http客戶端抓取遠程url的方法

    go語言實現(xiàn)一個簡單的http客戶端抓取遠程url的方法

    這篇文章主要介紹了go語言實現(xiàn)一個簡單的http客戶端抓取遠程url的方法,實例分析了Go語言http操作技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-03-03
  • Go語言學習之數(shù)組的用法詳解

    Go語言學習之數(shù)組的用法詳解

    數(shù)組是相同數(shù)據(jù)類型的一組數(shù)據(jù)的集合,數(shù)組一旦定義長度不能修改,數(shù)組可以通過下標(或者叫索引)來訪問元素。本文將通過示例詳細講解Go語言中數(shù)組的使用,需要的可以參考一下
    2022-04-04
  • Golang?sync.Once實現(xiàn)單例模式的方法詳解

    Golang?sync.Once實現(xiàn)單例模式的方法詳解

    Go?語言的?sync?包提供了一系列同步原語,其中?sync.Once?就是其中之一。本文將深入探討?sync.Once?的實現(xiàn)原理和使用方法,幫助大家更好地理解和應用?sync.Once,需要的可以參考一下
    2023-05-05
  • go?打包運行文件在windows,liunx運行

    go?打包運行文件在windows,liunx運行

    這篇文章主要介紹了go?打包運行文件在windows,liunx運行的相關(guān)資料,需要的朋友可以參考下
    2023-11-11

最新評論

重庆市| 新泰市| 汝阳县| 漾濞| 博客| 车险| 玛曲县| 交城县| 南平市| 新巴尔虎左旗| 南木林县| 沿河| 区。| 永顺县| 顺昌县| 忻州市| 长春市| 江门市| 肥乡县| 怀仁县| 灵璧县| 措美县| 逊克县| 潢川县| 会泽县| 连江县| 达孜县| 尼勒克县| 定远县| 垫江县| 梁平县| 武冈市| 潼关县| 洛浦县| 麟游县| 南阳市| 渑池县| 双流县| 高碑店市| 鹿泉市| 自治县|