Форум программистов, компьютерный форум, киберфорум
C++ Qt
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.94/78: Рейтинг темы: голосов - 78, средняя оценка - 4.94
 Аватар для calculon
14 / 14 / 2
Регистрация: 18.07.2012
Сообщений: 79

Сокеты и QThread - как корректно завершить поток

07.07.2014, 08:32. Показов 15051. Ответов 29
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Пишу клиент с использованием QTcpSocket. Вынес разбор принимаемых сообщений в отдельный поток, но из-за заморочек с объектами qt никак не соображу как корректно его завершить. К тому же не отправляются сообщения из других потоков. Буду благодарен если кто подскажет как наиболее корректно построить такую конструкцию.

Код клиента:
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
class QManagementClient : public QThread
{
    Q_OBJECT
 
public:
    explicit QManagementClient(QObject* parent = 0);
    virtual ~QManagementClient();
    void send(const QString& message);
    void connectToHost(const QString& hostName, quint16 port);
    void disconnectFromHost();
 
protected:
    void run();
 
signals:
    void connected();
    void disconnected();
 
private:
    bool m_quit;
    quint16 m_port;
    QString m_hostName;
    QMutex m_mutex;
    QTcpSocket* m_socket;
    ManagementProtocol m_protocol;
};
 
QManagementClient::QManagementClient(QObject* parent) : QThread(parent)
{
}
 
QManagementClient::~QManagementClient()
{
    disconnectFromHost();
}
 
void QManagementClient::send(const QString& message)
{
    m_socket->write(message).toLocal8Bit());
}
 
 
void QManagementClient::connectToHost(const QString& hostName, quint16 port)
{
    QMutexLocker locker(&m_mutex);
    m_hostName = hostName;
    m_port = port;
    m_quit = false;
    if (!isRunning())
        start();
}
 
void QManagementClient::run()
{
    m_mutex.lock();
    QString serverName = m_hostName;
    quint16 serverPort = m_port;
    m_mutex.unlock();
 
    m_socket = new QTcpSocket();
    m_socket->connectToHost(serverName, serverPort);
    if (!m_socket->waitForConnected(1000))
    {
        delete m_socket;
        m_socket = 0;
        return;
    }
 
    emit connected();
 
    QString data;
    while (!m_quit)
    {
        if (m_socket->waitForReadyRead())
    {
        // Чего то делаем
    }       
    }
 
    m_socket->disconnectFromHost();
 
    emit disconnected();
 
    delete m_socket;
    m_socket = 0;
}
 
void QManagementClient::disconnectFromHost()
{
    m_mutex.lock();
    m_quit = true;
    m_socket->disconnectFromHost();
    m_mutex.unlock();
 
    wait();
}
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
07.07.2014, 08:32
Ответы с готовыми решениями:

Как правильно завершить поток QThread и выйти из него?
Здравствуйте, форумчане! Есть GUI-приложение, кнопка в окне приложения запускает цикл обмена данными с com-портом. Цикл длительный так...

Как корректно завершить поток
Написал программу-терминал для работы с последовательным портом с учетом нашей специфики работы. По сути программа читает порт и выводит...

Как корректно завершить COM порт?
Здравствуйте. Пишу программу на С++ CLR для считывания с устройства пакетов данных. Но тут напоролся на проблему, которую не могу...

29
327 / 230 / 55
Регистрация: 30.05.2014
Сообщений: 682
08.07.2014, 12:35
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от calculon Посмотреть сообщение
Ведь при таком подходе приходится фактически дублировать сигналы и слоты в нескольких классах
На самом деле - нет. Класс Connecton должен реализовывать логику обмена по сети, т.е. это слой между сокетом и GUI, не просто тупо чтение-запись.
0
Эксперт С++
 Аватар для Avazart
8489 / 6156 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
08.07.2014, 14:32
Цитата Сообщение от Avazart Посмотреть сообщение
Как я понимаю можно сделать так:
1. В основном потоке создать сокет.
2. В основном потоке создать "рабочий" класс который будет обрабатывать примем данных (со слотами соотвующими)
3. В основном потоке создать класс QThread и через moveToThread() передать "рабочий" класс в него.
4. Связать сигналы сокета и "рабочего" класса через эвент луп потока.
Мда видимо такой подход не работает.
Сокет и объект которому посылаются сигналы от него должны существовать в одном потоке:
Цитата Сообщение от calculon Посмотреть сообщение
"ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 506a68. Receiver '' (of type 'QNativeSocketEngine') was created in thread 5449a0"
У меня приложение тоже упало, но без вывода ошибки.

Собственно такая штука разочаровывает, ибо видимо не все так гладко и удобно как пишут о сигналах и слотах в книгах .
0
327 / 230 / 55
Регистрация: 30.05.2014
Сообщений: 682
08.07.2014, 14:33
Цитата Сообщение от Avazart Посмотреть сообщение
Мда видимо такой подход не работает.
Работает другой - сокет должен создаваться в методе run потока, либо должен быть членом "рабочего" класса.
0
Эксперт С++
 Аватар для Avazart
8489 / 6156 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
08.07.2014, 14:36
Цитата Сообщение от uglyPinokkio Посмотреть сообщение
аботает другой - сокет должен создаваться в методе run потока, либо должен быть членом "рабочего" класса.
Ну так а какого, я же через сигнал/слот, должно быть пофиг, сигнал слот должны обеспечить между поточное взаимодействие по логике вещей.
Сообщения от сокета должны попадать эвент луп потока а от туда передаваться в класс-работник.

C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
 
    client= new QTcpSocket(this);
    thread= new QThread(this);
    worker= new Worker;
    worker->moveToThread(thread);
 
    connect(client,SIGNAL(readyRead()),
            worker,SLOT(readyRead())); // Qt::QueuedConnection
 
    thread->start();
}
0
327 / 230 / 55
Регистрация: 30.05.2014
Сообщений: 682
08.07.2014, 14:53
Цитата Сообщение от Avazart Посмотреть сообщение
Ну так а какого
Ну вот это не ко мне .

C++
1
client->moveToThread(thread);
Не спасает?

Добавлено через 4 минуты
Я как-то привык через Event между потоками связи делать.
0
Эксперт С++
 Аватар для Avazart
8489 / 6156 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
08.07.2014, 15:17
Хотя не, это я начудил, пытался сделать в потоке :
C++ (Qt)
1
2
3
4
5
6
7
8
9
void Worker::readyRead()
{
  QTcpSocket* client= qobject_cast<QTcpSocket*>(sender());
  Q_ASSERT(client);
  QByteArray content= client->readAll(); // Не хорошо !!!
  QTextCodec *codec = QTextCodec::codecForName("cp1251");
  QString enContent = codec->toUnicode(content.data());
 // qDebug()<< enContent;
}
Вероятно правильно делать через промежуточный сигнал/слот (виджета) , но как то криво тогда будет выглядеть.
Так все же лучше все в один поток помещать.

Собственно весьма не нравится мне эта асинхронность в Qt.
0
327 / 230 / 55
Регистрация: 30.05.2014
Сообщений: 682
08.07.2014, 15:30
Цитата Сообщение от Avazart Посмотреть сообщение
Собственно весьма не нравится мне эта асинхронность в Qt.
Это не в Qt, это в потоках в принципе. Ну и никто не мешает выкинуть нафиг цикл обработки сообщений и всю работу сделать в методе run с <del>блекджеком и</del> с callback.
0
Эксперт С++
 Аватар для Avazart
8489 / 6156 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
08.07.2014, 15:52
Цитата Сообщение от uglyPinokkio Посмотреть сообщение
Это не в Qt, это в потоках в принципе. Ну и никто не мешает выкинуть нафиг цикл обработки сообщений и всю работу сделать в методе run с <del>блекджеком и</del> с callback.
Ну QTcpSocket сам по себе асинхронный, т.е в нем уже есть поток, кроме того нам приходится все равно создавать дополнительный поток для обработки, т.е. у нас получается 2 потока вместо одного и в дабавок запутанный нелинейный код.

Не по теме:

Т.е. я имею ввиду если бы работали например в С++Builder c блокирующем Indy - то был бы один поток где делалось бы и чтение и обработка.



Добавлено через 6 минут
Кстати на счет bool и остановки потока, такую тему я поднимал Завершение потока (разбор кода "Циклы в отдельном потоке")
0
327 / 230 / 55
Регистрация: 30.05.2014
Сообщений: 682
08.07.2014, 16:37
Цитата Сообщение от Avazart Посмотреть сообщение
Кстати на счет bool и остановки потока
IMO - безопасны без синхронизации только операции, выполняющиеся за один (условно) такт процессора. Под Win это Atomic - функции, теперь вот еще в стандарте есть соответствующие типы. Для присвоения bool это не гарантируется.
0
 Аватар для calculon
14 / 14 / 2
Регистрация: 18.07.2012
Сообщений: 79
09.07.2014, 05:18  [ТС]
Цитата Сообщение от uglyPinokkio Посмотреть сообщение
На самом деле - нет. Класс Connecton должен реализовывать логику обмена по сети, т.е. это слой между сокетом и GUI, не просто тупо чтение-запись.
Согласен, в итоге так и получилось. Но такие вещи как установление, разрыв и ошибка соединения все равно пробрасывать придется.
Цитата Сообщение от uglyPinokkio Посмотреть сообщение
Работает другой - сокет должен создаваться в методе run потока, либо должен быть членом "рабочего" класса.
Код компактнее если сокет будет внутри обертки, а не снаружи. Реализовал именно так. Все работает, но в целом все равно не нравится. Уж больно похоже на высокотехнологичный костыль.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
09.07.2014, 05:18
Помогаю со студенческими работами здесь

Как корректно завершить чужое приложение?
Мне надо завершить чужое приложени, чтоб оно типо завершило свои действие сохранило то че надо. Я не имеею хандл это приложение а только...

Как завершить поток?
Проблема в том, что создается поток, который нужно завершить нажатием кнопки public void button1_Click(object sender, EventArgs e) ...

Как завершить поток
Как завершить поток, начатый _beginthread( Thread, 0, NULL );

Как из одной программы корректно завершить другую?
Добрый день! Возникла проблема: одна программа запускает другую, а когда завершает свою работу хотелось бы, чтобы запущенная...

Как корректно завершить вывод отчета в Excel?
Подскажите, как корректно завершить вывод отчета в Excel? Я объявляю новый объект, открываю лист, формирую отчет, он выводится на экран -...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
30
Ответ Создать тему
Новые блоги и статьи
Транскрипция 55-минутного видео через Whisper: WhisperDesktop облажался, спас Google Colab[
anaschu 01.06.2026
Понадобилось получить текст из свежезагруженного видео на YouTube. Казалось бы, задача на пять минут. Заняла полтора часа. Делюсь опытом — может кому пригодится последовательность решений. . . .
21 мат мед. Планы на развитие модели здравоСохранения
anaschu 01.06.2026
AnyLogic: план развития симуляционной модели рабочего коллектива — динамический абсентеизм, реальные данные, три сценария сравнения Продолжаю серию постов о дискретно-событийной модели рабочего. . .
20. Мат мед. Абсентеизм как отдельный тип простоя
anaschu 29.05.2026
Апдейт модели: исправленные баги, абсентеизм и новые механизмы Продолжаю развивать ранее описанную модель рабочего коллектива на AnyLogic. За последние несколько дней был проведён серьёзный. . .
19. здоровье, усталость и психотип работника влияют на производительность предприятия, и наоборот, производительность на здоровье, усталось и психотип
anaschu 28.05.2026
Дискретно-событийная модель рабочего коллектива на AnyLogic: здоровье, выгорание, психотипы и микростимуляция Привет, коллеги. Хочу поделиться итогами нескольких недель работы над симуляционной. . .
"Прокси" для последовательного порта
Eddy_Em 28.05.2026
Эту штуку написал я достаточно давно. Но сейчас вот понадобилось настроить датчик грозы, но при этом не отключать его от "метеодемона". Соответственно, надо запустить этот "прокси": метеодемон будет. . .
Рефакторинг программы уравнивания.
Massaraksh7 26.05.2026
Пример по предыдущей записи в блоге. Но, надо заметить, что, во-первых, там оптимизация не только математики, но и работы с базой данных, и с графами, а во-вторых, это ещё не всё.
Использование TThread в Lazarus для математических вычислений.
Massaraksh7 25.05.2026
Производя рефакторинг своих программ на предмет ускорения их работы, обратил внимание на такой аспект, как сокращение времени матвычислений. Дело в том, что приходится работать с большими матрицами. . .
Модель здравосохранения 18. Чем здоровее работник, тем быстрее выгорает
anaschu 24.05.2026
Имитационная модель корпоративного здравоохранения: что показывает математика Сегодня в модели рабочего коллектива на AnyLogic появились три новые механики — выгорание через накопленную усталость,. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru