C++编程:多线程优先级介绍

    线程优先级是操作系统调度器决定线程执行顺序的重要依据。在CPU资源紧张时,合理设置线程优先级可以确保关键任务(如数据采集)获得足够的处理时间。

线程优先级基础概念

1. 优先级的作用

  • 决定线程获取CPU时间的比例

  • 影响操作系统调度器的决策

  • 高优先级线程可以抢占低优先级线程

2. 优先级范围

  • Windows: 0-31 (实际通过API常量访问)

  • Linux: 1-99 (数值越大优先级越高)

  • 通用优先级分类:

    • 空闲优先级

    • 低优先级

    • 普通优先级(默认)

    • 高优先级

    • 实时优先级

Windows平台线程优先级设置

1. 完整API方法

#include <windows.h>
#include <thread>

void SetThreadPriorityWin(std::thread& thread, int priority) {
    HANDLE handle = thread.native_handle();
    
    // 首先设置进程优先级类(可选)
    SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
    
    // 设置线程优先级
    BOOL success = SetThreadPriority(handle, priority);
    
    if (!success) {
        DWORD error = GetLastError();
        // 错误处理...
    }
}

2. Windows优先级常量详解

优先级常量数值描述
THREAD_PRIORITY_IDLE-15最低优先级
THREAD_PRIORITY_LOWEST-2很低优先级
THREAD_PRIORITY_BELOW_NORMAL-1低于正常
THREAD_PRIORITY_NORMAL0正常(默认)
THREAD_PRIORITY_ABOVE_NORMAL1高于正常
THREAD_PRIORITY_HIGHEST2很高优先级
THREAD_PRIORITY_TIME_CRITICAL15实时优先级

3. 进程优先级类与线程优先级的关系

// 进程优先级类(需在设置线程优先级前调用)
SetPriorityClass(GetCurrentProcess(), priority_class);
进程优先级类描述
IDLE_PRIORITY_CLASS系统空闲时运行
BELOW_NORMAL_PRIORITY_CLASS低于正常
NORMAL_PRIORITY_CLASS正常(默认)
ABOVE_NORMAL_PRIORITY_CLASS高于正常
HIGH_PRIORITY_CLASS高优先级
REALTIME_PRIORITY_CLASS实时优先级(慎用)

Linux平台线程优先级设置

1. 完整API方法

#include <pthread.h>
#include <sched.h>
#include <thread>

void SetThreadPriorityLinux(std::thread& thread, int priority) {
    pthread_t handle = thread.native_handle();
    
    sched_param sch_params;
    int policy;
    
    // 获取当前调度策略
    pthread_getschedparam(handle, &policy, &sch_params);
    
    // 设置优先级 (1-99, 需要root权限)
    sch_params.sched_priority = priority;
    
    int ret = pthread_setschedparam(handle, SCHED_FIFO, &sch_params);
    if (ret != 0) {
        // 错误处理...
    }
}

2. Linux调度策略

调度策略描述
SCHED_OTHER标准时间共享策略(默认)
SCHED_FIFO先进先出实时策略
SCHED_RR轮转实时策略
SCHED_BATCH批处理作业
SCHED_IDLE极低优先级后台作业

3. 典型优先级值

  • 普通应用: 0 (SCHED_OTHER)

  • 高优先级: 50-80 (SCHED_FIFO/SCHED_RR)

  • 最高优先级: 99 (需要root权限)

跨平台封装实现

1. 通用优先级枚举

enum class ThreadPriority {
    Idle,          // 仅当系统空闲时运行
    Lowest,        // 最低后台优先级
    BelowNormal,   // 低于标准优先级
    Normal,        // 默认优先级
    AboveNormal,   // 高于标准优先级
    Highest,       // 最高非实时优先级
    TimeCritical   // 实时优先级(慎用)
};

2. 完整跨平台实现

#include <thread>
#include <stdexcept>

#ifdef _WIN32
#include <windows.h>
#else
#include <pthread.h>
#include <sched.h>
#endif

class PriorityThread {
public:
    explicit PriorityThread(ThreadPriority priority = ThreadPriority::Normal)
        : priority_(priority) {}
    
    template <typename Function, typename... Args>
    void Start(Function&& f, Args&&... args) {
        thread_ = std::thread([this, f = std::forward<Function>(f), 
                              args...]() mutable {
            SetPriority(priority_);
            std::invoke(f, args...);
        });
    }
    
    void Join() { if (thread_.joinable()) thread_.join(); }
    
private:
    void SetPriority(ThreadPriority priority) {
#ifdef _WIN32
        int win_priority;
        switch (priority) {
            case ThreadPriority::Idle: 
                win_priority = THREAD_PRIORITY_IDLE; break;
            case ThreadPriority::Lowest: 
                win_priority = THREAD_PRIORITY_LOWEST; break;
            case ThreadPriority::BelowNormal: 
                win_priority = THREAD_PRIORITY_BELOW_NORMAL; break;
            case ThreadPriority::Normal: 
                win_priority = THREAD_PRIORITY_NORMAL; break;
            case ThreadPriority::AboveNormal: 
                win_priority = THREAD_PRIORITY_ABOVE_NORMAL; break;
            case ThreadPriority::Highest: 
                win_priority = THREAD_PRIORITY_HIGHEST; break;
            case ThreadPriority::TimeCritical: 
                win_priority = THREAD_PRIORITY_TIME_CRITICAL; break;
            default: 
                throw std::invalid_argument("Invalid priority level");
        }
        
        if (!SetThreadPriority(GetCurrentThread(), win_priority)) {
            throw std::runtime_error("Failed to set thread priority");
        }
#else
        int policy;
        sched_param sch_params;
        pthread_getschedparam(pthread_self(), &policy, &sch_params);
        
        // 对于非实时策略,优先级必须为0
        if (priority != ThreadPriority::Normal && 
            priority != ThreadPriority::Idle) {
            policy = SCHED_FIFO;  // 或SCHED_RR
        }
        
        switch (priority) {
            case ThreadPriority::Idle:
                policy = SCHED_IDLE;
                sch_params.sched_priority = 0;
                break;
            case ThreadPriority::Lowest:
                sch_params.sched_priority = 1;
                break;
            case ThreadPriority::BelowNormal:
                sch_params.sched_priority = 30;
                break;
            case ThreadPriority::Normal:
                policy = SCHED_OTHER;
                sch_params.sched_priority = 0;
                break;
            case ThreadPriority::AboveNormal:
                sch_params.sched_priority = 60;
                break;
            case ThreadPriority::Highest:
                sch_params.sched_priority = 80;
                break;
            case ThreadPriority::TimeCritical:
                sch_params.sched_priority = 99;
                break;
            default:
                throw std::invalid_argument("Invalid priority level");
        }
        
        if (pthread_setschedparam(pthread_self(), policy, &sch_params) != 0) {
            throw std::runtime_error("Failed to set thread priority (may need root)");
        }
#endif
    }
    
    std::thread thread_;
    ThreadPriority priority_;
};

总结

  1. 优先级设置时机

    • 最好在线程开始运行后立即设置优先级

    • 避免频繁修改优先级

  2. 实时优先级风险

    • 可能使系统不稳定

    • 可能导致其他关键任务饥饿

    • 需要谨慎使用

  3. 数据采集应用建议

    cpp

  1. // 相机采集线程示例
    PriorityThread camera_thread(ThreadPriority::Highest);
    camera_thread.Start([]{
        while (!stop_requested) {
            // 相机采集和处理代码
        }
    });
    
    // 数据处理线程使用普通优先级
    std::thread processing_thread([]{
        // 数据处理代码
    });
  2. 调试技巧

    • 使用top -H(Linux)或Process Explorer(Windows)查看线程优先级

    • 实现丢帧计数器监控优先级设置效果

    • 考虑使用CPU亲和性绑定核心

  3. 错误处理

    • 总是检查优先级设置函数的返回值

    • 准备回退方案(当设置失败时)

通过合理设置线程优先级,可以显著改善工业相机在高负载下的采集稳定性,但需要注意平衡系统整体性能。

高速数据采集丢图问题

当开多线程且CPU负载满时,工业相机容易出现丢图现象,这通常是由以下几个原因导致的:

可能原因分析

  1. CPU资源竞争:多线程占满CPU导致相机图像处理线程得不到足够资源

  2. 缓冲区溢出:高CPU负载导致图像缓冲区处理不及时

  3. 线程优先级问题:相机采集线程优先级不够高

  4. 硬件限制:相机接口带宽或处理能力不足

优化改善点

1. 优化线程管理

  • 为相机采集和处理分配专用核心(CPU亲和性设置)

  • 降低非关键线程的优先级

  • 使用线程池控制并发数量

2. 调整数据采集参数

// 示例:设置更大的缓冲区(具体API取决于相机SDK)
camera.SetBufferNumber(10);  // 增加缓冲区数量
camera.SetAcquisitionFrameRate(30.0);  // 适当降低帧率

3. 硬件优化

  • 使用带硬件触发功能的相机

  • 考虑使用带FPGA预处理功能的相机

  • 升级到更高速的接口(如CameraLink、CoaXPress)

4. 软件架构优化

  • 实现生产者-消费者模式,分离采集和处理

  • 使用双缓冲或环形缓冲技术

  • 考虑零拷贝技术减少内存操作

5. 系统级调整

  • 在Linux下使用实时内核(RT-Preempt)

  • 在Windows下提高相机线程优先级

  • 禁用CPU节能模式

调试建议

  1. 使用性能分析工具(如perf, VTune)找出瓶颈

  2. 监控数据采集设备SDK提供的丢帧计数器

  3. 逐步增加负载测试系统极限

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值