You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mac系统下Go语言设置可被OS线程继承的进程优先级及QoS访问方法

macOS下进程优先级设置与Apple QoS的Go语言实现

问题描述

在Ubuntu系统中,使用syscall.SetPriority配合syscall.PRGRP标志可设置进程优先级,且该优先级会被线程继承。但相同代码在Mac系统中无法正常工作,现咨询:

  1. Mac系统下是否有类似方式设置可被OS线程继承的进程优先级;
  2. 如何在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线程继承的优先级,可按以下方式操作:

  1. 使用syscall.PRIO_PROCESS设置进程优先级,macOS中进程优先级会被新创建的OS线程继承。
  2. 由于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 08:15:10