2014-10-13 2 views
0

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

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

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

ответ

1

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

Начнем с решения этого вопроса в первую очередь. Многие люди расскажут вам, что You're Doing it Wrong!

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

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

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

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

0

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

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, которая будет иметь функцию-член делает операции, которые вы хотите иметь в своем отдельном потоке. Вы moveQObjectQThread хотите выполнить операцию при изменении ее affinity и отправлять/получать сигналы от нее/от нее.

В таких случаях не нужно и не рекомендуется наследовать 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" 

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