QSharedMemory

QSharedMemory读写内存时,可以使用lock()实现同步。因此,如果同步完成,必须使用unlock()为共享内存区域解锁。

QSharedMemory可以使用attach()访问共享内存。可以通过指定参数来设置共享内存的访问模式。如果使用的是QSharedMemory::ReadOnly模式,则只能通过只读模式访问共享内存;使用QSharedMemory::ReadWrite模式则可以通过读写模式访问共享内存。

QSharedMemory拥有进程并提供可以返回共享内存区域指针的成员函数。在共享内存区域,成员函数constData()可以通过void类型返回进程正在使用的内存区域指针。创建共享时,QSharedMemory可以以字节为单位分配共享内存区域,还可以通过第二个参数设置函数attach()提供的模式。

QSharedMemory可以设置特定共享内存的固定键。函数setNativeKey()可以设置共享内存对象的键,该函数使用从属平台的共享内存的键进行相关设置。相反,使用函数setKey()可以设置与独立与平台的键。函数setKey()创建与平台本地键(Native Key)映射的键。

当使用这个类时,请注意以下平台差异:

  • Windows:QSharedMemory不“拥有”共享内存段。当所有连接到特定共享内存段的QSharedMemory实例的线程或进程销毁了它们的QSharedMemory实例或退出时,Windows内核会自动释放共享内存段。
  • Unix:QSharedMemory“拥有”共享内存段。当连接到特定共享内存段的QSharedMemory实例的最后一个线程或进程通过销毁其QSharedMemory实例而与该段分离时,Unix内核释放该共享内存段。但是,如果最后一个线程或进程在没有运行QSharedMemory析构函数的情况下崩溃,那么共享内存段将在崩溃时幸存下来。
  • HP-UX:每个进程只允许连接一个共享内存段。这意味着在HP-UX中,QSharedMemory不应该跨同一进程中的多个线程使用。

示例代码

Qt提供的示例,位置: ….. \Qt5.15\Examples\Qt-5.15.2\corelib\ipc\sharedmemory

  1. #include <QDialog>
  2. #include <QSharedMemory>
  3. #include "ui_dialog.h"
  4. class Dialog : public QDialog
  5. {
  6. Q_OBJECT
  7. public:
  8. Dialog(QWidget *parent = nullptr);
  9. public slots:
  10. void loadFromFile();
  11. void loadFromMemory();
  12. private:
  13. void detach();
  14. private:
  15. Ui::Dialog ui;
  16. QSharedMemory sharedMemory;
  17. };
  1. // 写共享内存
  2. /*
  3. 步骤:
  4. 1.检测该进程是否连接到共享内存段,如果连接,则将该进程与共享内存分离
  5. 2.从系统足够大的内存中得到一个新的共享内存段
  6. 3.锁定该共享内存段,以阻止第二个对话框进程访问,将缓冲区中的图片复制到共享内存段
  7. 4.将共享内存段解锁,以便其他进程连接
  8. */
  9. void Dialog::loadFromFile()
  10. {
  11. if (sharedMemory.isAttached())
  12. if (!sharedMemory.detach())
  13. ui.label->setText(tr("销毁共享内存失败"));
  14. ui.label->setText(tr("选择图片"));
  15. QString fileName = QFileDialog::getOpenFileName(0, QString(), QString(),
  16. tr("Images (*.png *.xpm *.jpg)"));
  17. QImage image;
  18. if (!image.load(fileName)) {
  19. ui.label->setText(tr("选择的文件不是图片,请重新选择.错误码为:%0")
  20. .arg(sharedMemory.errorString()));
  21. return;
  22. }
  23. ui.label->setPixmap(QPixmap::fromImage(image));
  24. // 将数据(图片)写入到 QDataStream中
  25. QBuffer buffer;
  26. buffer.open(QBuffer::ReadWrite);
  27. QDataStream out(&buffer);
  28. out << image;
  29. int size = buffer.size();
  30. if (!sharedMemory.create(size)) {
  31. ui.label->setText(tr("不能创建共享内存"));
  32. return;
  33. }
  34. sharedMemory.lock();
  35. char* to = (char*)sharedMemory.data();
  36. const char* from = buffer.data().data();
  37. memcpy(to, from, qMin(sharedMemory.size(), size));
  38. sharedMemory.unlock();
  39. }
  40. // 读共享内存
  41. /*
  42. 步骤:
  43. 1.将该进程与进程A创建的共享内存段绑定
  44. 2.锁定共享内存段,复制数据到缓冲区,然后写入到QImage中
  45. 3.将共享内存段解锁,然后该进程与共享内存段分离
  46. */
  47. void Dialog::loadFromMemory()
  48. {
  49. if (!sharedMemory.attach()) {
  50. ui.label->setText(tr("不能连接到共享内存,错误码信息为:%0")
  51. .arg(sharedMemory.errorString()));
  52. return;
  53. }
  54. // 读数据
  55. QBuffer buffer;
  56. QDataStream in(&buffer);
  57. QImage image;
  58. sharedMemory.lock();
  59. buffer.setData((char*)sharedMemory.constData(), sharedMemory.size());
  60. buffer.open(QBuffer::ReadOnly);
  61. in >> image;
  62. sharedMemory.unlock();
  63. sharedMemory.detach();
  64. ui.label->setPixmap(QPixmap::fromImage(image));
  65. }
  66. Dialog::Dialog(QWidget* parent)
  67. : QDialog(parent)
  68. , sharedMemory("QSharedMemoryExample")
  69. {
  70. ui.setupUi(this);
  71. connect(ui.loadFromFileButton, &QPushButton::clicked,
  72. this, &Dialog::loadFromFile);
  73. connect(ui.loadFromSharedMemoryButton, &QPushButton::clicked,
  74. this, &Dialog::loadFromMemory);
  75. setWindowTitle(tr("SharedMemory Example"));
  76. }
  77. void Dialog::detach()
  78. {
  79. if (!sharedMemory.detach())
  80. ui.label->setText(tr("Unable to detach from shared memory."));
  81. }