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

 线程间的通讯可以由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_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    QProcess        m_Proc;
	
	QSharedMemory	m_mem;
	

	static QSystemSemaphore			m_lockSrc;
	static QSystemSemaphore			m_lockDst;

	void			init();
	QString			read();
	void			write();
public slots:
    void            OnClickOK(void);
    void            OnRecvProc(void);
	void			OnClickSend(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_OBJECT

public:
    Client(QWidget *parent = 0);
    ~Client();

	thrd						m_thrd;
	QLineEdit		*			m_pEdt;
public slots:
	void					OnRecv(QByteArray);
protected:
	void					resizeEvent(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_OBJECT
public:
	thrd(QObject * parent = 0);
	~thrd();

	static QSystemSemaphore		m_lockSrc;
	static QSystemSemaphore		m_lockDst;
	QSharedMemory				m_mem;
	void						read();
signals:
	void			sigMsg(QByteArray);
protected:
	void			run();
};

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之后,读写彼此牵制,只能一来一去,就保证了同步。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值