Mac系统下Go语言设置可被OS线程继承的进程优先级及QoS访问方法
macOS下进程优先级设置与Apple QoS的Go语言实现
问题描述
在Ubuntu系统中,使用
syscall.SetPriority配合syscall.PRGRP标志可设置进程优先级,且该优先级会被线程继承。但相同代码在Mac系统中无法正常工作,现咨询:
- Mac系统下是否有类似方式设置可被OS线程继承的进程优先级;
- 如何在Go语言中访问Apple QoS。
附原Ubuntu可用代码:
package main import ( "fmt" "os" "runtime" "sync" "syscall" "time" ) func main() { // runtime.GOMAXPROCS(5) // Get the current process ID pid := os.Getpid() fmt.Printf("Current Process ID: %d\n", pid) // Set process priority to the lowest setProcessPriority(pid, syscall.PRIO_PGRP, 10) // Number of goroutines to spawn numGoroutines := 100 // Use a wait group to wait for all goroutines to finish var wg sync.WaitGroup wg.Add(numGoroutines) start := time.Now() // Spawn goroutines for i := 0; i < numGoroutines; i++ { go func(id int) { defer wg.Done() fmt.Printf("Goroutine %d started.\n", id) cpuIntensiveWork() fmt.Printf("Goroutine %d completed.\n", id) }(i) } // Wait for all goroutines to finish wg.Wait() elapsed := time.Since(start) fmt.Printf("All goroutines completed in %f\n.", elapsed.Seconds()) } func setProcessPriority(pid int, which int, priority int) { if runtime.GOOS == "windows" { fmt.Println("Setting process priority is not supported on Windows.") return } // Set process priority for Unix-like systems err := syscall.Setpriority(which, pid, priority) if err != nil { fmt.Println("Error setting process priority:", err) } } func cpuIntensiveWork() { // Simulate CPU-intensive work for i := 0; i < 100000; i++ { for j := 0; j < 10000; j++ { _ = j * j } _ = i * i } }
一、Mac系统下设置可继承的进程优先级
macOS的setpriority系统调用行为和Linux存在差异:
- Linux中
PRIO_PGRP会修改整个进程组的优先级,且新线程自动继承;但macOS中PRIO_PGRP仅修改进程组组长的优先级,无法覆盖组内所有进程。 - macOS的优先级范围是**-20(最高)到20(最低)**,和Linux的范围逻辑一致,但原代码中设置的
10并非最低优先级,最低应该用20。
要实现可被OS线程继承的优先级,可按以下方式操作:
- 使用
syscall.PRIO_PROCESS设置进程优先级,macOS中进程优先级会被新创建的OS线程继承。 - 由于Go采用M:N调度,goroutine会被调度到不同OS线程,需用
runtime.LockOSThread()将goroutine绑定到特定OS线程,确保优先级生效。
修改后的macOS兼容代码:
package main import ( "fmt" "os" "runtime" "sync" "syscall" "time" ) func main() { pid := os.Getpid() fmt.Printf("Current Process ID: %d\n", pid) // macOS下用PRIO_PROCESS设置进程最低优先级(20) setProcessPriority(pid, syscall.PRIO_PROCESS, 20) numGoroutines := 100 var wg sync.WaitGroup wg.Add(numGoroutines) start := time.Now() for i := 0; i < numGoroutines; i++ { go func(id int) { defer wg.Done() // 绑定goroutine到OS线程,确保继承进程优先级 runtime.LockOSThread() defer runtime.UnlockOSThread() fmt.Printf("Goroutine %d started.\n", id) cpuIntensiveWork() fmt.Printf("Goroutine %d completed.\n", id) }(i) } wg.Wait() elapsed := time.Since(start) fmt.Printf("All goroutines completed in %.2f seconds.\n", elapsed.Seconds()) } func setProcessPriority(pid int, which int, priority int) { if runtime.GOOS == "windows" { fmt.Println("Setting process priority is not supported on Windows.") return } // 适配不同系统的优先级范围 if runtime.GOOS == "darwin" { if priority < -20 || priority > 20 { fmt.Println("macOS优先级范围必须在-20到20之间") return } } else { if priority < -20 || priority > 19 { fmt.Println("Linux优先级范围必须在-20到19之间") return } } err := syscall.Setpriority(which, pid, priority) if err != nil { fmt.Println("设置进程优先级失败:", err) } } func cpuIntensiveWork() { for i := 0; i < 100000; i++ { for j := 0; j < 10000; j++ { _ = j * j } _ = i * i } }
二、Go语言中访问Apple QoS
Apple QoS(服务质量)是macOS/iOS中更精细的任务调度机制,比传统优先级更灵活。Go中可通过cgo调用底层API直接访问:
实现代码
package main /* #include <pthread.h> #include <stdint.h> // 定义Apple QoS级别常量 typedef uint32_t qos_class_t; const qos_class_t QOS_CLASS_USER_INTERACTIVE = 0x21; // 最高优先级,用于交互任务 const qos_class_t QOS_CLASS_USER_INITIATED = 0x19; // 用户主动触发的任务 const qos_class_t QOS_CLASS_DEFAULT = 0x15; // 默认级别 const qos_class_t QOS_CLASS_UTILITY = 0x11; // 后台工具类任务 const qos_class_t QOS_CLASS_BACKGROUND = 0x09; // 最低优先级,后台任务 int set_qos_class(qos_class_t qos) { return pthread_set_qos_class_self_np(qos, 0); } */ import "C" import ( "fmt" "runtime" "sync" "time" ) func main() { // 设置当前线程QoS为后台级别 err := setQoS(C.QOS_CLASS_BACKGROUND) if err != nil { fmt.Println("设置QoS失败:", err) return } numGoroutines := 100 var wg sync.WaitGroup wg.Add(numGoroutines) start := time.Now() for i := 0; i < numGoroutines; i++ { go func(id int) { defer wg.Done() // 绑定goroutine到OS线程,确保QoS生效 runtime.LockOSThread() defer runtime.UnlockOSThread() fmt.Printf("Goroutine %d 以后台QoS级别启动.\n", id) cpuIntensiveWork() fmt.Printf("Goroutine %d 完成.\n", id) }(i) } wg.Wait() elapsed := time.Since(start) fmt.Printf("所有goroutine完成,耗时: %.2f 秒.\n", elapsed.Seconds()) } func setQoS(qos C.qos_class_t) error { res := C.set_qos_class(qos) if res != 0 { return fmt.Errorf("QoS设置失败,错误码: %d", res) } return nil } func cpuIntensiveWork() { for i := 0; i < 100000; i++ { for j := 0; j < 10000; j++ { _ = j * j } _ = i * i } }
说明
- QoS级别从高到低为:
USER_INTERACTIVE>USER_INITIATED>DEFAULT>UTILITY>BACKGROUND - 必须用
runtime.LockOSThread()绑定goroutine到OS线程,否则Go runtime的调度会让goroutine在不同线程间切换,导致QoS设置失效
内容的提问来源于stack exchange,提问作者abt
相关产品推荐
相关产品推荐

