Qt线程/进程间通讯

Qt线程/进程间数据通讯

本文主要用于阐述说明Qt线程/进程间数据通讯相关内容

Qt线程间数据通讯

方法一 全局变量

优点:使用方便;

缺点:全局变量长时间占用内存,影响程序空间使用率,且全局变量修改影响整个程序,程序的安全性无法保证;

注意:使用时需根据实际状况,加入,防止多线程同时使用同一个变量导致异常;

方法二 信号槽

只有QObject类及其派生的类才能使用信号和槽的机制,在线程间使用信号槽进行通信时,槽函数必须使用元数据类型的参数;如果使用自定义的数据类型,需要在connect之前将其注册(qRegisterMetaType)为元数据类型

下面例子为使用自定义的数据类型作为信号槽传递的参数,使用方法如下:

// mystruct.h
typedef struct MYSTRUCT{
	unsigned char data1[4];
	int data2;
	double data3;
	...
}MYSTRUCT;

------------------------------------------------------------------

//mainwindow.h
... //表示无关代码shenglve
#include "testthread.h"
#include "mystruct.h"
#include <QMetaType>

class MainWindow : public QMainWindow
{
	Q_OBJECT
public:
	MainWindow(QWidget *parent = nullptr);
	void init();
	
public slots:
	void slot_receive(const MYSTRUCT mystruct)

private:
	TestThread *m_testThread;
...
}

------------------------------------------------------------------

//mainwindow.cpp
...
MainWindow::MainWindow(QWidget *parent)
	: QMainWindow(parent)
{
	init();
}

void Mainwindow::init()
{
	qRegisterMetaType<MYSTRUCT>("mystruct");
	//MYSTRUCT 为自定义数据类型
	//mystruct 为定义的名称

	m_testThread = new TestThread();
	connect(m_testThread, &TestThread::signal_send, this, &Mainwindow::slot_receive);
	m_testThread->start();
}

void Mainwindow::slot_receive(const MYSTRUCT mystruct)
{

}
...

------------------------------------------------------------------

//testthread.h
#include "mystruct.h"
...
class TestThread : public QThread
{
	Q_OBJECT
public:
	TestThread();
	virtual void run();

signals:
	void signal_send(const MYSTRUCT mystruct);

private:
	MYSTRUCT m_struct;
...
}
------------------------------------------------------------------

//testthread.cpp
...
TestThread::TestThread()
{
	m_struct.data1[0] = 1;
	m_struct.data1[1] = 1;
	m_struct.data1[2] = 1;
	m_struct.data1[3] = 1;
	m_struct.data2 = 2;
	m_struct.data3 = 3.0;
}

void run()
{
	while(1)
	{
		emit signal_send(m_struct);
		sleep(1000);
	}
}

上述例程中线程使用 run() 方式运行,还可以使用 movetothread()(各路大佬推荐使用)方式运行;

线程间用信号槽传递参数的话,要加const,因为const文字常量存在常量区中,生命周期和程序一样长,可以避免slot调用的时候参数的运行期已过造成引用无效;

connect函数的第五个参数
(1) Qt::AutoConnection
如果发射信号的线程和执行槽函数的线程是同一个线程,此时等同于Qt::DirectConnection;如果不在同一个线程,就等同于Qt::QueuedConnection,是connect函数的默认参数;
(2) Qt::DirectConnection
发射信号和执行槽函数由同一个线程(信号发射的线程)完成,信号发射后槽函数立马执行,执行完毕后才继续执行“emit信号”后面的代码,即“emit信号”是阻塞的;
(3) Qt::QueuedConnection
发射信号的线程和执行槽函数的线程不是在同一个线程,此时发射信号的线程不阻塞,马上返回,当执行槽函数的线程被CPU调度时,就会执行槽函数;
(4) Qt::BlockingQueuedConnection
和Qt::QueuedConnection基本一样,只是发射信号的线程会被阻塞,直到槽函数被执行完毕,如果使用这个属性,需确保发射信号的线程与执行槽函数的线程不同,否则将发生死锁;
(5) Qt::UniqueConnection
唯一关联,该类型可以和上面的类型通过“|”符号配合使用,同一个信号与同一个槽只能调用connect一次,不能多次调用;

Qt进程间数据通讯

方法一 共享内存

原理:两个个进程共用同一片物理内存
实现方式如下:

进程A(数据写入)

//processA.h
#include <QSharedMemory>

class ProcessA
{
...
public:
	ProcessA();
	~ProcessA();
	void writeButtonClicked();
private:
	QSharedMemory *m_shareMemory;
...
}
...

---------------------------------------------------------

//processA.cpp
...
ProcessA::ProcessA()
{
...
	m_shareMemory = new QSharedMemory();    //实例化QSharedMemory类
	m_shareMemory->setKey("TestKey");     	//通过setKey()设置标签名称;
	//判断当前实例化对象m_shareMemory是否已经与进程连接,如果已经连接,使用函数detach()将与进程分离。
	if(m_shareMemory->isAttached())
	{       	
	    m_shareMemory->detach();
    }
    
	//使用函数create()创建共享内存段,判断是否创建成功,若失败,打印错误信息并返回
	if(!m_shareMemory->create(50))       	
	{
	   	qDebug() << m_shareMemory->errorString();
	   	return ;
	}
...
}

ProcessA::~ProcessA()
{
...
	//注意用完以后退出程序时在析构函数中释放
	m_shareMemory->detach();			
	delete m_shareMemory;
	m_shareMemory = nullptr;
...
}

void ProcessA::writeButtonClicked()
{
	char testStr[50] = "This is my share memory!";
	m_shareMemory->lock();                //使用共享内存前需调用lock()将共享内存上锁
	char *destination = reinterpret_cast<char *>(m_shareMemory->data());
	const char *source = testStr;
	memcpy(destination, source, 50);      //将数据写入共享内存
	m_shareMemory->unlock();              //调用unlock()函数解锁
}
...

进程B(读取写入)

//processB.h
#include <QSharedMemory>
class ProcessB
{
...
public:
	ProcessB();
	~ProcessB();
	void readButtonClicked();
private:
	QSharedMemory *m_shareMemory;
...
}
...

---------------------------------------------------------

//processB.cpp
...
ProcessB::ProcessB()
{
...
	m_shareMemory = new QSharedMemory();    //实例化QSharedMemory类
    m_shareMemory->setKey("TestKey");       //通过函数setKey()设置标签名称;
    //连接共享内存与进程
    if(!m_shareMemory->attach())          
    {
        qDebug() << "attach m_shareMemory error!";
        return ;
    }
...
}

ProcessB::~ProcessB()
{
...
	m_shareMemory->detach();
	delete m_shareMemory;
	m_shareMemory = nullptr;
...
}

void ProcessB::readButtonClicked()
{
    char testStr[50];
    m_shareMemory->lock();                //使用共享内存前需调用lock()将共享内存上锁
    const char *source = (char*)m_shareMemory->constData();
    char *destination = testStr;
    memcpy(destination, source, 50);      //从共享内存里读取数据
    m_shareMemory->unlock();              //调用unlock()函数解锁
}
...

方法二 socket通信

在两个进程之间创建socket,自己定义一套通信协议,通过socket,实现两个进程间的数据传输,在此不做赘述,可以参考socket通信相关内容。

  • 2
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Qt中,线程通讯可以使用共享内存来实现。共享内存是一种跨进程共享数据的机制,可以在不同的线程传递数据。 Qt提供了QSharedMemory类来操作共享内存。下面是一个使用共享内存进行线程通讯的示例: ```cpp // 创建一个共享内存对象 QSharedMemory sharedMemory; // 设置共享内存的名称 sharedMemory.setKey("MySharedMemory"); // 在一个线程中写入数据到共享内存 if (sharedMemory.create(1024)) { sharedMemory.lock(); char* data = static_cast<char*>(sharedMemory.data()); // 在这里写入数据到共享内存 // 注意:要确保多个线程对共享内存的访问是互斥的,可以使用QMutex来实现互斥访问 sharedMemory.unlock(); } // 在另一个线程中读取共享内存中的数据 if (sharedMemory.attach()) { sharedMemory.lock(); char* data = static_cast<char*>(sharedMemory.data()); // 在这里读取共享内存中的数据 sharedMemory.unlock(); sharedMemory.detach(); } ``` 在上面的示例中,我们创建了一个名为"MySharedMemory"的共享内存对象,并设置了它的大小为1024字节。然后,在一个线程中写入数据到共享内存中,另一个线程则可以读取共享内存中的数据。 需要注意的是,在多个线程对共享内存进行读写时,需要确保访问的互斥性,以避免数据竞争。可以使用QMutex或其他同步机制来实现互斥访问。 另外,还可以使用信号与槽机制来实现线程通讯Qt提供了QObject::moveToThread()方法,可以将一个QObject对象移动到指定的线程中,从而实现线程的信号与槽的连接。这种方式相对于共享内存来说更加方便和安全。 希望以上信息能对你有帮助!如果还有其他问题,请随时提问。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值