消息队列及发布订阅

概述

消息的发布、订阅是应用进程进行数据交换的常用方式,对于进程没有角色限制,既可以扮演发布也可以扮演订阅角色;该模式是进程间通信的异步模式。

mq_send函数说明

函数 mq_send() 会将参数 msg_ptr 指向的内容发送给参数mqdes 指向的消息队列.
参数 msg_len 指定消息的大小(用字节数表示). 参数 msg_len 的值应该不大于消息队列的属性 mq_msgsize 的值, 否则函数执行失败.
如果指定的消息队列不为空, mq_send() 的操作类似将消息插入在消息队列的指定位置,指定位置是通过参数 msg_prio 来指定.
参数 msg_prio 值大的消息会被插入在参数 msg_prio值小的消息前;如果值相等,则插在后面. 参数 msg_prio 的值应该小于 {MQ_PRIO_MAX}.
如果指定的消息队列满了并且 O_NONBLOCK(mq_open中设置) 未被设置, 函数mq_send() 会阻塞, 一直到消息可以被插入队列或者函数 mq_send() 被信号中断.
如果有几个线程都在等待消息队列的空闲资源向, 在支持优先级调度的系统中, 优先级最高的线程先解除阻塞,如果优先级相同则等待时间最长的线程先解除阻塞;
不支持优先级调度的系统中, 哪一个等待线程解除阻塞是未说明的.
如果指定的消息队列满了并且 O_NONBLOCK被设置了,函数 mq_send() 会失败返回.

#include <mqueue.h>
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len,unsigned msg_prio);

mq_receive函数说明

函数 mq_send() 会将参数 msg_ptr 指向的内容发送给参数mqdes 指向的消息队列.
参数 msg_len 指定消息的大小(用字节数表示). 参数 msg_len 的值应该不大于消息队列的属性 mq_msgsize 的值, 否则函数执行失败.
如果指定的消息队列不为空, mq_send() 的操作类似将消息插入在消息队列的指定位置,指定位置是通过参数 msg_prio 来指定.
参数 msg_prio 值大的消息会被插入在参数 msg_prio值小的消息前;如果值相等,则插在后面. 参数 msg_prio 的值应该小于 {MQ_PRIO_MAX}.
如果指定的消息队列满了并且 O_NONBLOCK(mq_open中设置) 未被设置, 函数mq_send() 会阻塞, 一直到消息可以被插入队列或者函数 mq_send() 被信号中断.
如果有几个线程都在等待消息队列的空闲资源向, 在支持优先级调度的系统中, 优先级最高的线程先解除阻塞,如果优先级相同则等待时间最长的线程先解除阻塞;
不支持优先级调度的系统中, 哪一个等待线程解除阻塞是未说明的.
如果指定的消息队列满了并且 O_NONBLOCK被设置了,函数 mq_send() 会失败返回.

int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len,unsigned msg_prio);

测试代码

#include <fstream>
#include <iostream>
#include <thread>
#include <vector>

#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>

std::string kQueueName = "/test2";

class MessageQueue {
  private:
    mqd_t handle;
  public:
    MessageQueue(const std::string& name, int flags) {
      handle = mq_open(name.c_str(), flags);
      if (handle < 0) {
        throw std::runtime_error("Failed to open a queue for writing");
      }
    }

    MessageQueue(const std::string& name, int flags, int max_count, int max_size) {
      struct mq_attr attrs = { 0, max_count, max_size, 0 }; 
      handle = mq_open(name.c_str(), flags | O_CREAT, 0666, &attrs);
      if (handle < 0) {
        throw std::runtime_error("Failed to create a queue");
      }
    }

    ~MessageQueue() {
      mq_close(handle);
    }

    void Send(const char* data, size_t len) {
      if (mq_send(handle, data, len, 0) < 0) {
        throw std::runtime_error("Failed to send a message");
      }
    }

    void Receive(char* data, size_t len) {
      if (mq_receive(handle, data, len, 0) < len) {
        throw std::runtime_error("Failed to receive a message");
      }
    }
};

template<class T>
class Writer {
  private:
    MessageQueue queue;
  public:
    Writer(std::string& name):
      queue(name, O_WRONLY) {}

    void Write(const T& data) {
      queue.Send(reinterpret_cast<const char*>(&data), sizeof(data));
    }
};

template<class T>
class Reader {
  private:
    MessageQueue queue;
  public:
    Reader(std::string& name):
      queue(name, O_RDONLY) {}

    void Run() {
      T data;
      while(true) {
        queue.Receive(reinterpret_cast<char*>(&data), sizeof(data));
        Callback(data);
      }
    }

  protected:
    virtual void Callback(const T& data) = 0;
};

struct Message {
  int x, y;
};

std::ostream& operator<<(std::ostream& o, const Message& m) {
  o << "(x=" << m.x << ", y=" << m.y << ")";
}

class CoordLogger : public Reader<Message> {
  using Reader<Message>::Reader;

  protected:
    void Callback(const Message& data) override {
      std::cout << "Received coordinate " << data << std::endl;
    }
};

void DoWrites() {
  std::vector<Message> messages {{1, 0}, {0, 1}, {1, 1}, {0, 0}};
  Writer<Message> writer(kQueueName);
  for (const auto& m : messages) { 
    std::cout << "Write " << m << std::endl;
    writer.Write(m);
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
  }
}

void DoReads() {
  CoordLogger logger(kQueueName);
  logger.Run();
}


int main(int argc, char** argv) {
  MessageQueue q(kQueueName, O_WRONLY, 10, sizeof(Message));
  pid_t pid = fork();
  if (pid) {
    DoWrites();
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    kill(pid, SIGTERM);
  } else {
    DoReads();
  }
}

测试结果


我公司承接各类技术服务,主要聚焦于:stm32、单片机、嵌入式、QT应用开发、Web+Python+Django应用开发。欢迎合作。

  • 62
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

汉森教育

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值