14 / 14 / 2
Регистрация: 18.07.2012
Сообщений: 79
1

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

07.07.2014, 08:32. Показов 12365. Ответов 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
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
07.07.2014, 08:32
Ответы с готовыми решениями:

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

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

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

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

29
1400 / 1257 / 262
Регистрация: 10.11.2013
Сообщений: 3,763
07.07.2014, 17:15 2
1) не нужно предопределять run(), почитайте про moveToThread().
2) если будете использовать moveToThread(), то поток завершаем так:
C++ (Qt)
1
2
3
th->quit();
th->wait();
delete th;
Если все же run() - то нужно заводить отдельную булевую переменную которая будет обрывать while'ы.
3) Сигналы у вас не работают так как при наследовании run() нужно вручную запускать цикл событий, по умолчанию его нет.
0
Эксперт С++
8381 / 6142 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
07.07.2014, 17:23 3
Цитата Сообщение от RazrFalcon Посмотреть сообщение
Если все же run() - то нужно заводить отдельную булевую переменную которая будет обрывать while'ы.
ну желательно "атомарную" а не булеву.

И если нужно "резко" останавливать поток то вероятно все таки через определение через run(), ибо через цикл сообщений все не пустишь
0
1400 / 1257 / 262
Регистрация: 10.11.2013
Сообщений: 3,763
07.07.2014, 17:26 4
Avazart, будет небольшой прирост производительности, и только, если не ошибаюсь.
0
Эксперт С++
8381 / 6142 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
07.07.2014, 17:27 5
Цитата Сообщение от RazrFalcon Посмотреть сообщение
Avazart, будет небольшой прирост производительности, и только, если не ошибаюсь.
Проблема не в производительности, а в прерывании потока.

Кроме того я не совсем понимаю, сокеты и так асинхронные зачем их в поток.
0
1400 / 1257 / 262
Регистрация: 10.11.2013
Сообщений: 3,763
07.07.2014, 17:35 6
Цитата Сообщение от Avazart Посмотреть сообщение
Проблема не в производительности, а в прерывании потока.
Сколько видел кода - всегда bool. И никаких проблем не замечал.
0
Эксперт С++
8381 / 6142 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
07.07.2014, 17:44 7
Цитата Сообщение от RazrFalcon Посмотреть сообщение
Сколько видел кода - всегда bool.
Значит не тот код смотрели, но в теории атомарный или с крит секцией,мютексом.
Цитата Сообщение от RazrFalcon Посмотреть сообщение
И никаких проблем не замечал.
C потоками можно долго не замечать...

Как я понимаю можно сделать так:
1. В основном потоке создать сокет.
2. В основном потоке создать "рабочий" класс который будет обрабатывать примем данных (со слотами соотвующими)
3. В основном потоке создать класс QThread и через moveToThread() передать "рабочий" класс в него.
4. Связать сигналы сокета и "рабочего" класса через эвент луп потока.
0
1400 / 1257 / 262
Регистрация: 10.11.2013
Сообщений: 3,763
07.07.2014, 18:02 8
Цитата Сообщение от Avazart Посмотреть сообщение
Значит не тот код смотрели
В сорцах Qt тоже обычно bool. Хотя там попадается и ужас, который они сам не советуют использовать, типа moteToThread(this).
В общем про то что bool плох для остановки потока я ни разу не слышал. Можете поделится ссылкой на объяснение?
0
Эксперт С++
8381 / 6142 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
07.07.2014, 18:18 9
Цитата Сообщение от RazrFalcon Посмотреть сообщение
Можете поделится ссылкой на объяснение?
Да что там объяснять bool теоретически может быть не атомарным, кроме того он может оптимизироваться компилятором, поэтому иногда советуют заменять на volatile unsigned char, но как бы и это не гарантировано, поэтому и лучше или атомарные типы из нового стандарта или родные Qt-тишные или защита через объекты синхронизации (флаг окончания - разделяемый ресурс)

Добавлено через 9 минут
Насчет bool https://www.cyberforum.ru/post3370373.html
0
1400 / 1257 / 262
Регистрация: 10.11.2013
Сообщений: 3,763
07.07.2014, 18:30 10
Avazart, нда, забавно. Где-то читал что gcc сам оборачивает перемененные потока в мютексы.
0
Эксперт С++
8381 / 6142 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
07.07.2014, 18:49 11
Цитата Сообщение от RazrFalcon Посмотреть сообщение
Avazart, нда, забавно. Где-то читал что gcc сам оборачивает перемененные потока в мютексы.
Ну тут когда такие проверки частые идут через объекты синхр. черевато потерей скорости.

Добавлено через 17 минут
Вот в этой книге вроде видел моменты с остановкой потока и о использовании QThread
Qt4.7+. Практическое программирование на C++ Андрей Боровский

Почитать частично можно тут http://books.google.ru/books?i... &q&f=false

стр 176 - пример с QAnatomicInt
1
14 / 14 / 2
Регистрация: 18.07.2012
Сообщений: 79
08.07.2014, 06:45  [ТС] 12
Avazart, переписал под сигналы/слоты. Проблема с зависанием потока исчезла. Но теперь в дебаге вижу QSocketNotifier: socket notifiers cannot be enabled from another thread и программа падает при попытке дисконнекта из основного потока с "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"

Код:
Кликните здесь для просмотра всего текста
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
class QManagementClient : public QThread
{
    Q_OBJECT
 
public:
    explicit QManagementClient(QObject* parent = 0);
    virtual ~QManagementClient();
    void connectToHost(const QString& hostName, quint16 port);
    void disconnectFromHost();
 
protected:
    void run();
 
signals:
    void connected();
    void disconnected();
 
public slots:
    void readData();
    void onDisconnect();
    void send(const QString& message);
 
private:
    quint16 m_port;
    QString m_hostName;
    QTcpSocket* m_socket;
};
 
QManagementClient::QManagementClient(QObject* parent) : QThread(parent)
{
    moveToThread(this);
}
 
QManagementClient::~QManagementClient()
{
    wait();
}
 
void QManagementClient::send(const QString& message)
{
    m_socket->write(message.toLocal8Bit());
    m_socket->flush();
}
 
void QManagementClient::connectToHost(const QString& hostName, quint16 port)
{
    m_hostName = hostName;
    m_port = port;
 
    if (!isRunning())
        start();
}
 
void QManagementClient::run()
{
    m_socket = new QTcpSocket(this);
    connect(m_socket, SIGNAL(readyRead()), this, SLOT(readData()));
    connect(m_socket, SIGNAL(connected()), this, SIGNAL(connected()));
    connect(m_socket, SIGNAL(disconnected()), this, SLOT(onDisconnect()));
 
    m_socket->connectToHost(m_hostName, m_port);
    if (!m_socket->waitForConnected(1000))
    {
        delete m_socket;
        m_socket = 0;
        return;
    }
 
    emit connected();
 
    exec();
}
 
void QManagementClient::readData()
{
    qDebug() << m_socket->readAll();
}
 
void QManagementClient::onDisconnect()
{
    emit disconnected();
    exit(0);
}
 
void QManagementClient::disconnectFromHost()
{
    m_socket->disconnectFromHost();
}
0
327 / 230 / 55
Регистрация: 30.05.2014
Сообщений: 682
08.07.2014, 07:38 13
Цитата Сообщение от Avazart Посмотреть сообщение
Вот в этой книге вроде видел моменты с остановкой потока и о использовании QThread
Сложно и местами (про разницу в завершении потока в С и С++) спорно. Приостановка рабочего цикла потока элементарно делается через ожидание мютекса с таймаутом. Выход из цикла - через атомарную переменную, или через bool с синхронизацией доступа.

Цитата Сообщение от calculon Посмотреть сообщение
программа падает при попытке дисконнекта из основного потока
Как именно выглядит попытка? вызов метода disconnectFromHost() ?
0
14 / 14 / 2
Регистрация: 18.07.2012
Сообщений: 79
08.07.2014, 08:01  [ТС] 14
uglyPinokkio, да, с дисконнектом косяк, проблема была в вызове из основного потока, переделал под слот и все стало работать нормально, но проблема с QSocketNotifier никуда не делась и вылезает при чтении/записи.
0
Эксперт С++
8381 / 6142 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
08.07.2014, 10:32 15
calculon, Мда неожиданная ошибка, MoveThread выполняли ?

Добавлено через 1 минуту
Цитата Сообщение от uglyPinokkio Посмотреть сообщение
Приостановка рабочего цикла потока элементарно делается через ожидание мютекса с таймаутом.
Ну это не приостановка это "ожидание".
0
327 / 230 / 55
Регистрация: 30.05.2014
Сообщений: 682
08.07.2014, 10:49 16
Цитата Сообщение от Avazart Посмотреть сообщение
Ну это не приостановка это "ожидание".
Поток в состоянии ожидания процессорного времени не получает. По крайней мере под Win.

Добавлено через 8 минут
Цитата Сообщение от Avazart Посмотреть сообщение
Мда неожиданная ошибка
Что-то тоже без идей. read-то должен бы работать.
0
14 / 14 / 2
Регистрация: 18.07.2012
Сообщений: 79
08.07.2014, 11:46  [ТС] 17
uglyPinokkio, пришлось отказаться от наследования QThread и сделать дополнительный класс Connection, который находится в том же потоке что и сокет и является посредником во всех вызовах, пробрасывая сигналы класса Client. По мне так слишком витиевато, но ничего лучше на просторах сети я пока не нашел, только множество подобных веток на форумах без каких либо конкретных рекомендаций.
0
327 / 230 / 55
Регистрация: 30.05.2014
Сообщений: 682
08.07.2014, 11:51 18
Цитата Сообщение от calculon Посмотреть сообщение
пришлось отказаться от наследования QThread и сделать дополнительный класс Connection, который находится в том же потоке что и сокет и является посредником во всех вызовах, пробрасывая сигналы класса Client.
Я сам примерно так и делаю . Но IMO - тот вариант тоже имеет право на существование.
0
14 / 14 / 2
Регистрация: 18.07.2012
Сообщений: 79
08.07.2014, 11:58  [ТС] 19
uglyPinokkio, неужели нет более удобного и изящного способа? Ведь при таком подходе приходится фактически дублировать сигналы и слоты в нескольких классах...
0
Эксперт С++
8381 / 6142 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
08.07.2014, 12:05 20
Будет время, надо будет попробовать.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
08.07.2014, 12:05
Помогаю со студенческими работами здесь

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

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

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

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


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

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2022, CyberForum.ru