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;
}