Параллельный доступ к объекту QTcpSocket

Я создал класс, который наследует от QThread чтобы иметь поток, работающий с QTcpSocket объект. Когда этот поток запускается, он подключается к серверу: если в соединении отказано или соединение потеряно, поток пытается повторно подключиться к серверу, пытаясь восстановить соединение неограниченное количество раз. Другими словами, этот поток пытается сохранить соединение с указанным сервером.

QTcpSocket* объявлен как член атрибута _socket моего класса. Первая строка run() функция создает экземпляр _socket объект и пытается подключиться к серверу. Последняя строка run() вызовы функций _socket->disconnectFromHost(), Я зарегистрировал событие, отключенное от _socket объект, чтобы позвонить _socket->deleteLater(),

Эта тема, которую я создал, работает правильно. Теперь я бы добавил функцию для отправки данных на сервер: эта функция должна вызывать write() функция _socket объект, и он должен быть вызван другим потоком. Итак, я должен использовать мьютекс, чтобы использовать _socket объект?

2 ответа

класс, который наследуется от QThread

Начните с решения этого в первую очередь. Многие люди здесь скажут вам, что вы делаете это неправильно!

QThread - это скорее контроллер потоков, чем поток, поэтому, если вы не хотите изменить способ управления потоками в Qt, я рекомендую вам не наследовать его. Вместо этого следуйте методу, описанному в разделе Как на самом деле использовать QThread.

Я собираюсь предположить, что у вас есть хорошая причина использовать QTCpSocket в отдельном потоке, даже если он асинхронный.

Если у вас есть QTcpSocket в другом потоке и вы хотите вызвать функцию записи сокета, вы должны использовать механизм сигнал / слот, а не вызывать функцию объекта напрямую из другого потока.

Итак, подведя итог, прочитав отличную статью о том, как использовать QThread, проведите рефакторинг вашего кода, чтобы создать отдельный объект, производный от QObject, который можно переместить в новый поток. Затем используйте механизм сигнал / слот для отправки данных этому объекту, который затем может вызвать функцию записи сокета.

Если я правильно понял, вы хотите отправлять и получать сигналы из основного потока в ваш рабочий поток. Документация довольно хорошо объясняет, как это сделать:

class Worker : public QObject
{
    Q_OBJECT

public slots:
    void doWork(const QString &parameter) {
        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, Но если вы это сделаете, не забудьте позвонить exec() в run() функция-член, чтобы поток запускал свой собственный цикл событий (чтобы он мог обрабатывать асинхронные операции).

Так что нет, вам не нужны мьютексы для ваших операций. Циклы событий основного потока и рабочего потока могут красиво общаться, посылать сигналы друг другу и синхронизироваться.

Я рекомендую взглянуть на это решение Consume/Producer, которое синхронизирует 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 total\n", 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 total\n", 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"

Читайте здесь для получения дополнительной информации.

Другие вопросы по тегам