YOLOv8 + openVINO 多线程数据读写顺序处理

博客围绕多线程数据读写顺序处理展开,介绍了典型的生产者 - 消费者模型。多个工作线程从共享队列取数据处理,结果按原始顺序放入另一队列。还提及自定义多线程模板,确保数据读写正常,最后以ffmpeg读取数据用openVINO推理为例说明,保证数据顺序一致。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

多线程数据读写顺序处理

在这里插入图片描述

一个典型的生产者-消费者模型,在这个模型中,多个工作线程并行处理从共享队列中获取的数据,并将处理结果以保持原始顺序的方式放入另一个队列。

多线程处理模型,具体细节如下:

1.数据:数据里必须有个递增的标识符和一个结束标识(ending)
2. 读队列(安全队列):用于存放待处理的数据。

  1. 处理线程:每个线程都是一个死循环读数据-处理数据-写数据,它们被编号为1、2、3、4等。这些线程负责从读队列中取出数据进行处理。线程的结束:判断数据里的ending为true.

  2. 结果聚合:处理完成后,判断数据的递增的标识符,是否为全局的递增的标识符,如果相等 继续执行。以保持数据的一致性。

  3. 写队列(安全队列):用于处理好的数据按照读的顺序写入,写入数据到输出队列的顺序是保持一致的。

自定义设计多线程模版:

#include "queuestable.h"

#ifndef QUEUESTABLE_H
#define QUEUESTABLE_H

#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <atomic>
#include <type_traits>
#include <memory>

// 基本模板,用于一般类型 必须存在
template <typename T>
struct underlying_type_pointer {
   
    using type = T;
};

// 针对原生指针的特化 默认调用 基本模板
template <typename T>
struct underlying_type_pointer<T*>
{
   
    using type = T;
};
// 针对 std::shared_ptr 的特化 默认调用 基本模板
template <typename T>
struct underlying_type_pointer<std::shared_ptr<T>> {
   
    using type = T;
};

// 针对 std::unique_ptr 的特化 默认调用 基本模板
template <typename T>
struct underlying_type_pointer<std::unique_ptr<T>> {
   
    using type = T;
};

// 提取底层类型的辅助类型别名
template <typename T>
using underlying_type_pointer_t = typename underlying_type_pointer<T>::type;




// 检测是否为 std::shared_ptr
//基本模版用于一般类型 必须存在
template <typename T>
struct is_pointer : std::false_type {
   };
//  针对 原生指针* 的特化 默认调用 基本模板
template <typename T>
struct is_pointer<T*> : std::true_type {
   };

// 针对 std::shared_ptr 的特化 默认调用 基本模板
template <typename T>
struct is_pointer<std::shared_ptr<T>> : std::true_type {
   };

// 针对 std::unique_ptr 的特化 默认调用 基本模板
template <typename T>
struct is_pointer<std::unique_ptr<T>> : std::true_type {
   };

// 提取指针类型检查的布尔值别名
template <typename T>
constexpr bool is_pointer_v = is_pointer<T>::value;


//基本数据
struct BaseData
{
   
    /*
     * `递增的标识符`  sequence_number
     * 记录当前位置,保证数据顺序一致
     * 这是内部计数,不需要设置,设置也没用,内部会重新排列
     */
    int64_t  sequence_number = 0;

    //true 代表结尾  ,到数据结尾的时候,手动设置为true
    bool ending = false;

};

//定义一个线程安全的队列  T必须是指针类型
template <typename T>
class QueueStable
{
   

    // 首先检测 T 是否为原生指针,is_shared_ptr或者is_unique_ptr
    static_assert(is_pointer_v<T>, "T must be a pointer!");
    // 检测 T 是指向继承自 BaseData 的类的指针
    static_assert(std::is_base_of<BaseData, underlying_type_pointer_t<T>>::value, "T must be a pointer to a class derived from BaseData!");

    //std::is_pointer  只能判断 *  指针
    //static_assert(std::is_pointer<T>::value , "T must be a pointer!");
public:

    QueueStable() = default;
    QueueStable(const QueueStable<T>&) = delete;
    QueueStable& operator=(const QueueStable<T>&) = delete;

    QueueStable(unsigned int max_size)
    {
   
        m_max_size = max_size;
    }

    //设置存放数据的最大容量
    void set_max_size(unsigned int max_size)
    {
   
        m_max_size = max_size;
    }

    void push(const T& value)
    {
   
        std::unique_lock<std::mutex> lock(m_mutex);
        //队列大于m_max_size就阻塞
        m_cv.wait(lock, [&] {
    return m_queue.size() < m_max_size; }); //为 true 继续执行,否则,解锁-》等待。唤醒后,加锁
        if(value)
        {
   
                       //当前序号
            value->sequence_number = m_sequence_number;
            //下一个数据序号
            m_sequence_number += m_step_number;

        }
        else
        {
   
            // 打印异常信息
            std::cerr << "QueueStable: value cannot be nullptr." << std::endl;
            throw std::runtime_error("value cannot be nullptr.");
        }


        //入队
        m_queue.push(std::move(value));
        //m_queue.push(value);
        //解锁
        lock.unlock();
        m_cv.notify_one(); //唤醒另一个
    }
    T pop()
    {
   
        //加锁
        std::unique_lock<std::mutex> lock(m_mutex);
        m_cv.wait(lock, [&] {
    return !m_queue.empty(); }); //为 true 继续执行,否则,解锁-》等待。唤醒后,加锁
        //出队
        T data = std::move(m_queue.front());
        //T data = m_queue.front();
        m_queue.pop();
        //解锁
        lock.unlock();
        m_cv.notify_one(); //唤醒另一个

        return data;
    }

    T front() const
    {
   
        std::lock_guard<std::mutex> lock(m_mutex);
        if(!m_queue.empty())
        {
   
            return m_queue.front();
        }
        return nullptr;

    }


    //获得第一个数据序号值
    int64_t get_start_seq_num()
    {
   
        std::lock_guard<std::mutex> lock(m_mutex);

        //非空返回第一个数据
        if(!m_queue.empty())
        {
   
            return m_queue.front()->sequence_number;
        }

        //返回下一次将要递增的数据
        return m_sequence_number;
    }

    //获得最后一个数据序号值
    int64_t get_end_seq_num()
    {
   
        std::lock_guard<std::mutex> lock(m_mutex);
        return m_sequence_number;
    }


    //获取增加计数的步长
    int32_t get_step()
    {
   
        std::lock_guard<std::mutex> lock(m_mutex);
        return m_step_number;
    }

    //设置步长
    bool set_step(int32_t step)
    {
   
        std::lock_guard<std::mutex> lock(m_mutex);

        //数据非空,不能设置,会导致数据序号不是按m_step_number递增
        if(!m_queue.empty())
        {
   
            return false;
        }
        m_step_number = step;
        return true;

    }

    bool empty() const
    {
   
        std::lock_guard<std::mutex> lock(m_mutex);
        return m_queue.empty();
    }
    int size(
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值