利用QSystemSemaphore和QSharedMemory实现进程间通讯

来源:互联网 发布:淘宝文件夹是哪个位置 编辑:程序博客网 时间:2024/05/21 13:56

 线程间的通讯可以由QSemaphore调控,以保证各个线程对同一资源的访问不冲突。但是进程间的协调就不能利用QSemaphore,而要利用QSystemSemaphore。

此外,在同一进程内的各个线程之间可以用信号-槽机制通信,但是进程之间就不可以了。取而代之的是QSharedMemory。下面的两个程序test_process和ProcessClient运行在不同的进程中。前者为主进程,后者为子进程。主进程利用QProcess::start()启动子进程。QProcess::start(QString())的作用与在命令行输入命令类似。start的输入参数可以是一个exe文件的名字。这个exe文件在另一个进程中运行。当主进程结束,exe所在的子进程也随之结束。

先看主进程的代码:

头文件:

#ifndef MAINWINDOW_H#define MAINWINDOW_H#include <QMainWindow>#include <QProcess>#include <qfile.h>#include <qsystemsemaphore.h>#include <qsharedmemory.h>namespace Ui {class MainWindow;}class MainWindow : public QMainWindow{    Q_OBJECTpublic:    explicit MainWindow(QWidget *parent = 0);    ~MainWindow();    QProcess        m_Proc;QSharedMemorym_mem;static QSystemSemaphorem_lockSrc;static QSystemSemaphorem_lockDst;voidinit();QStringread();voidwrite();public slots:    void            OnClickOK(void);    void            OnRecvProc(void);voidOnClickSend(void);private:    Ui::MainWindow *ui;};#endif // MAINWINDOW_H

在头文件中,我定义了主进程向子进程写入数据的函数write(),也定义了读出子进程数据的函数read()。在实际应用中,我只用到了write()。

cpp文件:

#include "mainwindow.h"#include "ui_mainwindow.h"#include <QDebug>#include <qbuffer.h>MainWindow::MainWindow(QWidget *parent) :    QMainWindow(parent),    ui(new Ui::MainWindow){    ui->setupUi(this);    QObject::connect(&m_Proc, SIGNAL(readyRead()), this, SLOT(OnRecvProc()));    QObject::connect(ui->BtnOK, SIGNAL(clicked()), this, SLOT(OnClickOK()));QObject::connect(ui->BtnSend, SIGNAL(clicked()), this, SLOT(OnClickSend()));init();}MainWindow::~MainWindow(){    delete ui;}void MainWindow::init(){m_mem.setKey(QString("sharedMem"));if (m_mem.isAttached())      {          m_mem.detach();    }  m_mem.create(1024);}QSystemSemaphore MainWindow::m_lockSrc(QString("lockSrc"), 1, QSystemSemaphore::Create);QSystemSemaphore MainWindow::m_lockDst(QString("lockDst"), 0, QSystemSemaphore::Create);void MainWindow::OnClickOK(void){    QString qstrCmd = ui->lineEdit->text();    m_Proc.start(qstrCmd);}void MainWindow::OnClickSend(void){write();}void MainWindow::OnRecvProc(void){    QByteArray qba = m_Proc.readAll();    QString qstrFeedBack(qba);    ui->textEdit->setText(qstrFeedBack);}QString MainWindow::read()  {      QBuffer buffer;      QDataStream in(&buffer);      QString text;        m_mem.lock();      buffer.setData((char*)m_mem.constData(), m_mem.size());      buffer.open(QBuffer::ReadOnly);      in >> text;      m_mem.unlock();      qDebug() << "WriteSharedMemory:: Read:" << text;      return text;  } void MainWindow::write( )  {       QBuffer buffer;buffer.open( QBuffer::ReadWrite );      QDataStream out( &buffer );  QString text = ui->lineEdit->text();    out << text;      int size = buffer.size();        if(m_mem.size()<size)      {          qDebug() << "共享内存空间不够!";          return ;      }            if(m_lockSrc.acquire())    {// Write into the shared memory  m_mem.lock();  char *to = (char*)m_mem.data();  const char *from = buffer.data().data();  memcpy( to, from, qMin( m_mem.size(), size ) );  m_mem.unlock();  m_lockDst.release();  qDebug() << "WriteSharedMemory:: Write:" << text;  }}  

再看子进程。它包括两个类:Client和thrd。本来只要client一个类即可接收主线程发来的数据。但是实验发现那样会很卡顿。所以建立一个QThread的派生类--thrd。thrd负责接收主线程的数据。收到后,再利用信号槽机制传给Client,显示出来。通过开启一个线程的方式避免卡顿。

先看Client头文件:

#pragma once#include <QWidget>#include <qlineedit.h>#include <QResizeEvent>#include "thrd.h"class Client : public QWidget{    Q_OBJECTpublic:    Client(QWidget *parent = 0);    ~Client();thrdm_thrd;QLineEdit*m_pEdt;public slots:voidOnRecv(QByteArray);protected:voidresizeEvent(QResizeEvent *);};
Client.cpp:

#include "client.h"#include <QDebug>#include <qbuffer.h>#include <QMessageBox>Client::Client(QWidget *parent)    : QWidget(parent){    QMessageBox msg;    msg.setText("start");    msg.exec();m_pEdt = new QLineEdit(this);QObject::connect(&m_thrd, SIGNAL(sigMsg(QByteArray)), this, SLOT(OnRecv(QByteArray)));m_thrd.start();}Client::~Client(){m_thrd.terminate();}void Client::OnRecv(QByteArray qba){m_pEdt->setText(QString(qba));}void Client::resizeEvent(QResizeEvent *e){m_pEdt->setGeometry(width() / 10, height()/10, width() * 0.8, 20);}


thrd.h

#pragma once#include <qthread.h>#include <qsystemsemaphore.h>#include <qsharedmemory.h>class thrd : public QThread{Q_OBJECTpublic:thrd(QObject * parent = 0);~thrd();static QSystemSemaphorem_lockSrc;static QSystemSemaphorem_lockDst;QSharedMemorym_mem;voidread();signals:voidsigMsg(QByteArray);protected:voidrun();};

thrd.cpp

#include "thrd.h"#include <qbuffer.h>#include <qdatastream.h>thrd::thrd(QObject * parent) : QThread(parent){m_mem.setKey(QString("sharedMem"));}thrd::~thrd(){}QSystemSemaphore thrd::m_lockSrc(QString("lockSrc"), 1, QSystemSemaphore::Open);QSystemSemaphore thrd::m_lockDst(QString("lockDst"), 0, QSystemSemaphore::Open);void thrd::run(){while(true){read();msleep(1000);}}void thrd::read()  {      if(m_mem.isAttached())      {          //qDebug() << "ReadSharedMemory:: haved attached.";      }      else       {          if(!m_mem.attach())           {              QSharedMemory::SharedMemoryError m = m_mem.error();              return;            }          else          {              //qDebug() << "ReadSharedMemory:: attach success.";          }      }        QBuffer buffer;      QDataStream in(&buffer);      QString text;    if(m_lockDst.acquire())    {m_mem.lock();  buffer.setData((char*)m_mem.constData(), m_mem.size());  buffer.open(QBuffer::ReadOnly);  in >> text;  //清空缓存  char* to = (char*)m_mem.data();  memset(to,0,m_mem.size());  m_mem.unlock();    m_lockSrc.release();        QByteArray qba = text.toLatin1();emit sigMsg(qba);}}  


运行结果:

先启动test_process.exe。在它的第一个文本框里输入ProcessClient.exe的路径,然后点击OK,启动子进程窗口。随后,你可以在父进程的同一个文本框里输入其他字符,然后点击"send",将内容发给子进程。子进程收到后,会将信息显示在自己的文本框里。在本例中,我发送的消息是12345


在本例子中,主进程给QSharedMemory命名,并且赋予它1024字节的空间。还创建了2个信号量。

子进程的共享内存QSharedMemory必须使用主进程的共享内存一样的名字,并且要使用同名的信号量。但使用时,只要open即可,不需要create。

最后解释为什么要使用两个QSystemSemaphore,而不是一个。在前面的博客<<自己对互斥和同步的理解>>一文中,我那下棋做比方解释同步和互斥。假如我只用一个semaphore,主进程写完一次数据后,可能不会等子进程读取,就会再写一次数据。这是因为系统的调度随机的,不会保证写入-读出严格交替。但是使用了2个semaphore之后,读写彼此牵制,只能一来一去,就保证了同步。

0 0
原创粉丝点击