154 lines
3.4 KiB
Go
154 lines
3.4 KiB
Go
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"net/http"
|
||
"os/exec"
|
||
"runtime"
|
||
"sync"
|
||
"time"
|
||
)
|
||
|
||
// 验证Hand Off触发场景,覆盖所有核心触发类型
|
||
func main() {
|
||
// 限制P数量为1,放大Hand Off效果(若不触发,G2需等待G1完成)
|
||
runtime.GOMAXPROCS(1)
|
||
fmt.Printf("当前P的数量: %d\n", runtime.GOMAXPROCS(0))
|
||
|
||
// 强制GC和调度器初始化
|
||
runtime.GC()
|
||
time.Sleep(10 * time.Millisecond)
|
||
|
||
// 1. 阻塞式网络I/O触发Hand Off
|
||
testBlockNetIO()
|
||
fmt.Println("=== 分割线 ===")
|
||
|
||
// 2. time.Sleep()触发Hand Off
|
||
testTimeSleep()
|
||
fmt.Println("=== 分割线 ===")
|
||
|
||
// 3. os/exec执行外部命令触发Hand Off
|
||
testExecCommand()
|
||
}
|
||
|
||
// 场景1:阻塞式网络I/O(HTTP请求)
|
||
func testBlockNetIO() {
|
||
var wg sync.WaitGroup
|
||
// 控制执行顺序,避免调度随机性
|
||
g1Start := make(chan struct{})
|
||
g2Exec := make(chan struct{})
|
||
|
||
// G1:执行阻塞式HTTP请求
|
||
wg.Add(1)
|
||
go func() {
|
||
defer wg.Done()
|
||
close(g1Start)
|
||
fmt.Println("G1: 开始执行阻塞式HTTP请求")
|
||
// 阻塞式网络I/O
|
||
_, err := http.Get("https://www.baidu.com")
|
||
if err != nil {
|
||
fmt.Printf("G1: HTTP请求异常: %v\n", err)
|
||
}
|
||
fmt.Println("G1: HTTP请求完成")
|
||
close(g2Exec)
|
||
}()
|
||
|
||
// 等待G1启动后,启动G2
|
||
<-g1Start
|
||
time.Sleep(50 * time.Millisecond)
|
||
|
||
// G2:验证Hand Off生效(若触发则立即执行)
|
||
wg.Add(1)
|
||
go func() {
|
||
defer wg.Done()
|
||
fmt.Println("G2: 开始执行(验证网络I/O触发Hand Off)")
|
||
<-g2Exec
|
||
fmt.Println("G2: 执行完成")
|
||
}()
|
||
|
||
wg.Wait()
|
||
}
|
||
|
||
// 场景2:time.Sleep()触发Hand Off
|
||
func testTimeSleep() {
|
||
var wg sync.WaitGroup
|
||
g1Sleep := make(chan struct{})
|
||
|
||
// G1:执行time.Sleep()
|
||
wg.Add(1)
|
||
go func() {
|
||
defer wg.Done()
|
||
close(g1Sleep)
|
||
fmt.Println("G1: 开始执行time.Sleep(1秒)")
|
||
time.Sleep(1 * time.Second)
|
||
fmt.Println("G1: sleep完成,恢复执行")
|
||
}()
|
||
|
||
// 等待G1进入sleep后,启动G2
|
||
<-g1Sleep
|
||
time.Sleep(50 * time.Millisecond)
|
||
|
||
// G2:验证Hand Off生效
|
||
wg.Add(1)
|
||
go func() {
|
||
defer wg.Done()
|
||
fmt.Println("G2: 开始执行(验证time.Sleep触发Hand Off)")
|
||
}()
|
||
|
||
wg.Wait()
|
||
}
|
||
|
||
// 场景3:os/exec执行外部命令
|
||
func testExecCommand() {
|
||
var wg sync.WaitGroup
|
||
g1Exec := make(chan struct{})
|
||
g2Started := make(chan struct{})
|
||
|
||
// G1:执行外部sleep命令
|
||
wg.Add(1)
|
||
go func() {
|
||
defer wg.Done()
|
||
close(g1Exec) // 通知G1已启动
|
||
fmt.Println("G1: 执行外部命令 sleep 1s")
|
||
|
||
// 关键:在阻塞前让出处理器,确保调度器有机会切换
|
||
runtime.Gosched()
|
||
|
||
// 跨平台命令执行
|
||
var cmd *exec.Cmd
|
||
if runtime.GOOS == "windows" {
|
||
fmt.Println("检测到Windows系统,使用timeout命令")
|
||
cmd = exec.Command("powershell", "-command", "Start-Sleep -Seconds 1")
|
||
} else {
|
||
cmd = exec.Command("sleep", "1")
|
||
}
|
||
err := cmd.Run()
|
||
if err != nil {
|
||
fmt.Printf("G1: 命令执行错误: %v\n", err)
|
||
}
|
||
fmt.Println("G1: 外部命令执行完成")
|
||
}()
|
||
|
||
// 等待G1启动
|
||
<-g1Exec
|
||
time.Sleep(10 * time.Millisecond) // 短暂等待确保G1进入阻塞
|
||
|
||
// G2:验证Hand Off生效
|
||
wg.Add(1)
|
||
go func() {
|
||
defer wg.Done()
|
||
close(g2Started)
|
||
fmt.Println("G2: 开始执行(验证os/exec触发Hand Off)")
|
||
}()
|
||
|
||
// 等待G2启动确认
|
||
select {
|
||
case <-g2Started:
|
||
fmt.Println("验证成功: G2在G1阻塞期间执行")
|
||
case <-time.After(500 * time.Millisecond):
|
||
fmt.Println("警告: G2未及时执行,可能未触发Hand Off")
|
||
}
|
||
|
||
wg.Wait()
|
||
}
|