利用zeromq,python端发送图片,C++端接收并多线程处理图片(2)

文章介绍了如何在C++中构建一个线程安全的队列用于在多线程环境中共享数据。具体实现包括一个线程安全队列模板类,用于存储和检索OpenCV的Mat对象。同时,文章展示了两个线程函数,一个作为消费者从队列中取出图像进行处理,另一个作为生产者从ZeroMQ套接字接收图像数据并将其推入队列。主函数创建了多个消费者线程和一个生产者线程协同工作。
摘要由CSDN通过智能技术生成

C++端为服务端server

1.先建立一个线程安全的队列

#include <zmq.hpp>
#include <opencv2/opencv.hpp>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <atomic>
// 线程安全的队列
template<typename T>
class ThreadSafeQueue {
public:
    ThreadSafeQueue(): stop(false) {}

    // 队列中取出一个元素,如果队列为空则阻塞等待
    T pop() {
        std::unique_lock<std::mutex> lock(m_mutex);
        //m_cond.wait(lock, [this] { return !m_queue.empty(); });
        if(m_cond.wait_for(lock, std::chrono::seconds(5), [this] { return !m_queue.empty(); })){//设置超时处理方法,超过五秒视为发送端停止发送数据
            T item = m_queue.front();
            m_queue.pop();
            return item;
        }
        else{
            T empty_data;//停止发送数据后向队列中插入空数据
            stop = true;//stop原子置为true
            return empty_data;
        }

    }

    // 在队列的尾部添加一个元素
    void push(const T& item) {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_queue.push(item);
        lock.unlock();
        m_cond.notify_one();
    }

    bool getstop(){
        bool stop_val =  stop.load(); //std::atomic<bool> 要避免调用拷贝构造函数
        return stop_val;
    }

    void changestop(bool value){
        stop = value;
    }

private:
    std::queue<T> m_queue;
    std::mutex m_mutex;
    std::condition_variable m_cond;
    std::atomic<bool> stop;//多线程处理数据停止标志
};

2.创建消费线程入口函数

// 线程函数,循环取出队列中的数据
void consumer(ThreadSafeQueue<cv::Mat>& q) {
    int count = 0;
    std::thread::id threadId = std::this_thread::get_id(); // 获取当前线程 ID
    std::ostringstream oss;
    oss << threadId; // 将线程 ID 输出到 ostringstream 中
    std::string threadIdString = oss.str(); // 从 ostringstream 中获取字符串
    while (true) {
        if(!q.getstop()){
            std::string filename = "./results/thread_" + threadIdString + "_frame_" + std::to_string(count) + ".jpg";
            cv::Mat item = q.pop();
            if(!item.empty()){
                //开始处理数据,添加想要的对图片的操作,例如目标检测推理等
                cv::imwrite(filename, item);
                std::cout << "consumer thread " << std::this_thread::get_id() << ": pop item " << count << std::endl;
                count +=1;
            }
            else{
                std::cout<<" consumer data is empty, exit consumer"<<std::endl;
                continue;
            }
        }
        else{
            std::cout<<"stop is true, consumer data stop"<<std::endl;
            break;
        }
    }
}

3.创建接收数据的线程入口函数 

// 线程函数,循环往队列中添加数据
void producer(ThreadSafeQueue<cv::Mat>& q) {
    //int i = 0;
    zmq::context_t context(1);
    zmq::socket_t socket(context, ZMQ_PULL);
    socket.bind("tcp://*:5557");
    //int batch_size = 4;
    int count = 0;
    // 设置超时时间为 10 秒
    zmq::pollitem_t items[] = {{static_cast<void*>(socket), 0, ZMQ_POLLIN, 0}};
    int timeout = 10000; // 单位为毫秒
    std::cout<<"start listening"<<std::endl;

    while (true) {
        int rc = zmq::poll(items, 1, timeout);
        if (rc == -1) {
            std::cerr << "Poll error." << std::endl;
            break;
        }
        if (items[0].revents & ZMQ_POLLIN) {//超过10秒接收不到数据,关闭socket
            std::cout<<"start rec"<<std::endl;
            
            zmq::message_t message;
            socket.recv(&message);

            //接收到空数据停止接收
            if (message.size() == 0) {
                std::cout << "rec data size is 0, End of image rec queue" << std::endl;
                break;  
            }
            std::vector<uchar> buffer(message.size());
            memcpy(buffer.data(), message.data(), message.size());

            cv::Mat image = cv::imdecode(buffer, cv::IMREAD_COLOR);
            //cv::imshow("Received Image", image);

            //将接收的数据放入队列
            q.push(image);
            
            std::cout << "producer thread " << std::this_thread::get_id() << ": push item " << count << std::endl;
            //std::this_thread::sleep_for(std::chrono::milliseconds(1000));
            count +=1;
            //cv::waitKey(0);
        }

        else {
            // 超时关闭 socket
            std::cerr << "Timeout reached, closing socket." << std::endl;
            break;
        }
    }
    socket.close();

4.main函数调用

int main() {

    ThreadSafeQueue<cv::Mat> q;

    // 创建 4 个 consumer 线程和 1 个 producer 线程
    std::vector<std::thread> consumers;
    for (int i = 0; i < 4; ++i) {
        consumers.emplace_back(consumer, std::ref(q));
    }
    std::thread producers(producer, std::ref(q));

    // 等待所有线程结束后退出程序
    for (auto& t : consumers) {
        t.join();
    }
    producers.join();

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值