对QTcpSocket对象的并发访问
Concurrent access to a QTcpSocket object
我创建了一个从QThread
继承的类,以便让线程与QTcpSocket
对象一起运行。当这个线程启动时,它会连接到服务器:如果连接被拒绝或连接丢失,线程会尝试重新连接到服务器,尝试无限次重新连接。换句话说,这个线程试图保持与指定服务器的连接。
QTcpSocket*
被声明为我的类的属性成员_socket
。run()
函数的第一行实例化_socket
对象并尝试连接到服务器。run()
函数的最后一行调用_socket->disconnectFromHost()
。我注册了_socket
对象断开连接的事件,以便调用_socket->deleteLater()
。
我创建的这个线程工作正常。现在我要添加一个函数来向服务器发送数据:这个函数应该调用_socket
对象的write()
函数,并且应该由另一个线程调用。那么,我应该使用互斥来使用_socket
对象吗?
从QThread继承的类
首先解决这个问题。这里的很多人会告诉你你做错了!
QThread更像是一个线程控制器,而不是一个线程,所以除非你想改变Qt管理线程的方式,否则我建议你不要从它继承。相反,按照how to Really,Truly Use QThread中描述的方法。
我假设您有充分的理由在单独的线程中使用QTCpSocket,即使它是异步的。
如果您在另一个线程上有一个QTcpSocket,并且想要调用套接字的写函数,则应该使用信号/插槽机制,而不是直接从另一个螺纹调用对象的函数。
总之,在阅读了关于如何使用QThread的优秀文章之后,重构代码以创建一个独立的对象,该对象派生自QObject,可以移动到一个新的线程。然后使用signal/slot机制将数据发送到该对象,然后该对象可以调用套接字的写函数。
如果我理解正确,您希望从主线程向工作线程发送和接收信号。文档很好地解释了如何做到这一点:
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString ¶meter) {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
您必须创建一个QObject
,它将有一个成员函数来执行您希望在单独线程中执行的操作。您将此QObject
移动到QThread
,以便在更改其相关性时执行操作,并向其发送/从其接收信号。
在这种情况下,没有必要也不建议继承QThread
。但是,如果这样做,不要忘记在run()
成员函数中调用exec()
,这样线程就可以启动自己的事件循环(这样它就可以处理异步操作)。
所以不,您的操作不需要互斥。主线程和工作线程的事件循环可以很好地进行通信、相互发送信号和同步。
我建议您看看这个Consume/Productor解决方案,它同步2个线程,由Bradley Hughes
在2006年创建,当时QThread::run()
默认情况下没有启动事件循环。以下是他的例子的更新版本:
#include <QtCore>
#include <stdio.h>
enum {
Limit = 123456,
BlockSize = 7890
};
class Producer : public QObject
{
Q_OBJECT
QByteArray data;
int bytes;
public:
inline Producer() : bytes(0) { }
public slots:
void produce()
{
int remaining = Limit - bytes;
if (remaining == 0) {
emit finished();
return;
}
// this will never happen
if (data.size() != 0)
qFatal("Producer: Consumer failed to consume!");
int size = qMin(int(BlockSize), remaining);
bytes += size;
remaining -= size;
data.fill('Q', size);
printf("Producer: produced %d more bytes, %d of %d totaln", size, bytes, Limit);
emit produced(&data);
}
signals:
void produced(QByteArray *data);
void finished();
};
class Consumer : public QObject
{
Q_OBJECT
int bytes;
public:
inline Consumer() : bytes(0) { }
public slots:
void consume(QByteArray *data)
{
// this will never happen
if (data->size() == 0)
qFatal("Consumer: Producer failed to produce!");
int remaining = Limit - bytes;
int size = data->size();
remaining -= size;
bytes += size;
data->clear();
printf("Consumer: consumed %d more bytes, %d of %d totaln", size, bytes, Limit);
emit consumed();
if (remaining == 0)
emit finished();
}
signals:
void consumed();
void finished();
};
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
// create the producer and consumer and plug them together
Producer producer;
Consumer consumer;
producer.connect(&consumer,
SIGNAL(consumed()),
SLOT(produce()));
consumer.connect(&producer,
SIGNAL(produced(QByteArray *)),
SLOT(consume(QByteArray *)));
// they both get their own thread
QThread producerThread;
producer.moveToThread(&producerThread);
QThread consumerThread;
consumer.moveToThread(&consumerThread);
// start producing once the producer's thread has started
producer.connect(&producerThread,
SIGNAL(started()),
SLOT(produce()));
// when the consumer is done, it stops its thread
consumerThread.connect(&consumer,
SIGNAL(finished()),
SLOT(quit()));
// when consumerThread is done, it stops the producerThread
producerThread.connect(&consumerThread,
SIGNAL(finished()),
SLOT(quit()));
// when producerThread is done, it quits the application
app.connect(&producerThread,
SIGNAL(finished()),
SLOT(quit()));
// go!
producerThread.start();
consumerThread.start();
return app.exec();
}
#include "main.moc"
阅读此处了解更多信息。
- 通过方法访问结构
- 使用不带参数的函数访问结构元素
- 如果我只是不访问queue_front节点的子节点,而是将它们推到队列中呢?还是BFS吗
- 用于访问容器<T>数据成员的正确 API
- 访问者访问变体并返回不同类型时出错
- 尝试通过多个向量访问变量时,向量下标超出范围
- 无法访问嵌套类.类的使用无效
- 写入位置0x0000000C时发生访问冲突
- 我们可以访问一个不存在的联盟的成员吗
- C++从另一个类访问公共静态向量的正确方法是什么
- 用于在并发环境中访问 MMIO 的软件模式
- Libuv:保护事件循环免受并发访问
- 并发内存访问减慢系统速度
- SQLite中的并发访问
- C/C++中的并发日志文件访问
- openMP - 并发访问变量和原子
- 对QTcpSocket对象的并发访问
- 隔离类的并发/非并发访问数据成员
- 避免并发访问变量
- 从多个进程并发访问共享内存DLL