Форум программистов, компьютерный форум, киберфорум
C++ Qt
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.80/41: Рейтинг темы: голосов - 41, средняя оценка - 4.80
2 / 2 / 2
Регистрация: 27.02.2016
Сообщений: 247

Как сделать вторичный поток с постоянный жизненным циклом и управлением из главного потока

24.11.2019, 14:52. Показов 9201. Ответов 113
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Имеется объект, который живёт в другом потоке и выполняет различные задачи по запросам или выполняет повторяющиеся задачи.

Более приземленное описание. Через протокол modbus RTU опрашиваются датчики. Датчиков 247 штук. Используются 3 команды, чтение регистров, запись регистров, чтение ID. Управление датчиками может быть ручное или автоматическое. При ручном режиме принимаем данные из главного потока и передаем на датчик, после ответа датчика передаем данные в главный поток для записи в usb устройство и отображение на экране. При автоматическом режиме управляем датчиками по заданному алгоритму и отсылаем ответы датчиков в главный поток для записи на usb устройство и отображения данных.

Вот не знаю как лучше реализовать это.
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
24.11.2019, 14:52
Ответы с готовыми решениями:

Как усыплять дочерний поток реализуя управление элементами из главного потока методом Invoke
как управлять элементами из главного потока в дочернем? Если напишите методом Invoke, то объясните тогда - как заставять в определённых...

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

VC++6.0, как из порожденного потока прочитать переменную главного потока?
Visual C++ 6.0 SP5 Простое диалоговое окно, (MFC) созданное мастером среды VC По нажатию на кнопку создаю поражденный поток, в...

113
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
01.12.2019, 17:42
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Alex_Golubev Посмотреть сообщение
SensorInfo для чего ?
Это класс, инкапсулирующий данные о каждом сенсоре (адрес, его данные). Добавьте туда все поля что нужны согласно вашей логике.
0
2737 / 892 / 331
Регистрация: 10.02.2018
Сообщений: 2,135
01.12.2019, 18:03
Alex_Golubev, могу предложить помесь на STL и QT. STL-ый поток, STL-ые примитивы синхронизации, прямые вызовы функций управления, а вот уведомления о завершении операций через QT-шную очередь сообщений. Что-то вроде такого...
mainwindow.h
C++ (Qt)
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
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
 
#include <QMainWindow>
#include "thread.h"
 
class MainWindow : public QMainWindow
{
    Q_OBJECT
 
public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
 
public slots:
    void StartOne();
    void StartAll();
    void Stop();
 
    void DoneOne(int id, QByteArray answer);
    void DoneAll(QByteArray answer);
 
protected:
    Thread* thread;
};
 
#endif // MAINWINDOW_H

mainwindow.cpp
C++ (Qt)
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
#include "mainwindow.h"
#include <QVBoxLayout>
#include <QPushButton>
#include <QMessageBox>
 
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    thread = new Thread;
 
    QPushButton* btnReadOne = new QPushButton("read one");
    QPushButton* btnReadAll = new QPushButton("read all");
    QPushButton* btnStopRead = new QPushButton("stop read");
 
    QVBoxLayout* l = new QVBoxLayout();
    l->addWidget(btnReadOne);
    l->addWidget(btnReadAll);
    l->addWidget(btnStopRead);
    QWidget* w = new QWidget;
    w->setLayout(l);
    setCentralWidget(w);
 
    connect(btnReadOne, &QPushButton::clicked, this, &MainWindow::StartOne);
    connect(btnReadAll, &QPushButton::clicked, this, &MainWindow::StartAll);
    connect(btnStopRead, &QPushButton::clicked, this, &MainWindow::Stop);
 
    connect(thread, &Thread::readOneDone, this, &MainWindow::DoneOne);
    connect(thread, &Thread::readAllDone, this, &MainWindow::DoneAll);
}
 
MainWindow::~MainWindow()
{
    delete thread;
}
 
void MainWindow::StartOne()
{
    thread->readOne(2);
}
 
void MainWindow::StartAll()
{
    thread->readAll();
}
 
void MainWindow::Stop()
{
    thread->stopRead();
}
 
void MainWindow::DoneOne(int id, QByteArray answer)
{
    QMessageBox box;
    box.setWindowTitle("Done One");
    box.setText(answer);
    box.exec();
}
 
void MainWindow::DoneAll(QByteArray answer)
{
    QMessageBox box;
    box.setWindowTitle("Done All");
    box.setText(answer);
    box.exec();
}

thread.h
C++ (Qt)
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
#ifndef THREAD_H
#define THREAD_H
 
#include <QObject>
 
#include <thread>
#include <mutex>
#include <condition_variable>
 
enum ETask
{
    keTaskNone,
    keTaskReadOne,
    keTaskReadAll,
    keTaskStopRead,
    keTaskTerminate,
};
 
struct STaskState
{
    ETask next;
    int nextId;
 
    ETask current;
    int currentId;
    int doneCount;
    int maxCount;
};
 
class Thread : public QObject
{
    Q_OBJECT
 
public:
    explicit Thread(QObject *parent = 0);
    ~Thread();
 
    bool readOne(int id);
    bool readAll();
    void stopRead();
 
    STaskState getState() { std::lock_guard<std::mutex> lck(mutex); return state; }
 
signals:
    void readOneDone(int id, QByteArray answer);
    void readAllDone(QByteArray answer);
 
protected:
    void loop();
    bool waitTask();
    QByteArray execOne(int id);
 
protected:
    std::mutex mutex;
    std::thread thread;
    std::condition_variable convar;
    STaskState state;
    QByteArray answerAll;
};
 
#endif // THREAD_H

thread.cpp
C++ (Qt)
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include "thread.h"
#include <chrono>
 
Thread::Thread(QObject *parent) : QObject(parent)
{
    state.next = keTaskNone;
    state.nextId = 0;
 
    state.current = keTaskNone;
    state.currentId = 0;
    state.doneCount = 0;
    state.maxCount = 0;
 
    thread = std::thread(&Thread::loop, this);
}
 
Thread::~Thread()
{
    mutex.lock();
    state.next = keTaskTerminate;
    convar.notify_one();
    mutex.unlock();
 
    if (thread.joinable())
        thread.join();
}
 
bool Thread::readOne(int id)
{
    std::lock_guard<std::mutex> lck(mutex);
 
    // предыдущий запрос ещё не начал обрабатываться
    if (state.next != keTaskNone)
        return false;
 
    state.next = keTaskReadOne;
    state.nextId = id;
 
    convar.notify_one();
 
    return true;
}
 
bool Thread::readAll()
{
    std::lock_guard<std::mutex> lck(mutex);
 
    // предыдущий запрос ещё не начал обрабатываться
    if (state.next != keTaskNone)
        return false;
 
    state.next = keTaskReadAll;
    state.nextId = 0;
 
    convar.notify_one();
 
    return true;
}
 
void Thread::stopRead()
{
    std::lock_guard<std::mutex> lck(mutex);
 
    state.next = keTaskStopRead;
 
    convar.notify_one();
}
 
void Thread::loop()
{
    while (waitTask())
    {
        if (state.current == keTaskReadOne)
        {
            QByteArray answer = execOne(state.currentId);
 
            mutex.lock();
            state.doneCount++;
            mutex.unlock();
 
            emit readOneDone(state.currentId, answer);
        }
        else if (state.current == keTaskReadAll)
        {
            QByteArray answer = execOne(state.doneCount);
 
            if (state.doneCount > 0)
                answerAll += ",";
            answerAll.append(answer);
 
            mutex.lock();
            state.doneCount++;
            mutex.unlock();
 
            if (state.doneCount >= state.maxCount)
                emit readAllDone(answerAll);
        }
    }
}
 
bool Thread::waitTask()
{
    std::unique_lock<std::mutex> lck(mutex);
    while ((state.next == keTaskNone) && (state.doneCount == state.maxCount))
        convar.wait(lck);
 
    if (state.next == keTaskTerminate)
        return false;
 
    if (state.next == keTaskReadOne)
    {
        state.current = keTaskReadOne;
        state.currentId = state.nextId;
        state.doneCount = 0;
        state.maxCount = 1;
    }
    else if (state.next == keTaskReadAll)
    {
        answerAll.clear();
        state.current = keTaskReadAll;
        state.currentId = 0;
        state.doneCount = 0;
        state.maxCount = 200;
    }
    else if (state.next == keTaskStopRead)
    {
        state.current = keTaskStopRead;
        state.currentId = 0;
        state.doneCount = 0;
        state.maxCount = 0;
    }
    else if (state.next == keTaskNone)
    {
        if (state.current == keTaskReadAll)
        {
        }
    }
 
    state.next = keTaskNone;
 
    return true;
}
 
QByteArray Thread::execOne(int id)
{
    std::this_thread::sleep_for(std::chrono::microseconds(80));
    return QByteArray().setNum(id);
}
0
2 / 2 / 2
Регистрация: 27.02.2016
Сообщений: 247
02.12.2019, 14:41  [ТС]
Вот сделал класс iodevice
Кликните здесь для просмотра всего текста
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef IODEVICE_H
#define IODEVICE_H
#include <QtCore>
#include <modbus/modbus.h>
#include <errno.h>
 
class IODevice
{
private:
 
public:
    IODevice();
    int readData(int address_device, int addr, int nb, unsigned short int* tab_reg);
    int writeData(int address_device, int addr, int nb, unsigned short int *tab_reg);
    int readId(int address_device, int size, unsigned char* text);
};
 
#endif // IODEVICE_H


Кликните здесь для просмотра всего текста
C++ (Qt)
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include "iodevice.h"
 
IODevice::IODevice()
{
 
}
 
int IODevice::readData(int address_device, int addr, int nb, unsigned short *tab_reg)
{
    int rc = 0;
    unsigned char counter = 0;
    // 'E' - event work mode, 8 - data bits, 1 - stop bit - serial port configuration
    modbus_t* my_modbus = modbus_new_rtu("/dev/ttyAPP1", 4800, 'E', 8, 1);
    uint32_t old_response_to_sec;
    uint32_t old_response_to_usec;
    modbus_get_response_timeout(my_modbus, &old_response_to_sec, &old_response_to_usec);
    modbus_set_response_timeout(my_modbus, 0, 30000);
 
    if(modbus_connect(my_modbus)  == 0){
 
    modbus_set_slave(my_modbus, address_device);
 
    while(1)
    {
    rc = modbus_read_input_registers(my_modbus, addr, nb,tab_reg);
    if(rc != -1)
    {
    break;
    }
    counter ++;
    if(counter == 3){
    modbus_close(my_modbus);
    modbus_free(my_modbus);
    return (-1);
    break;
    }
    }
 
    }
    modbus_close(my_modbus);
    modbus_free(my_modbus);
    return(rc);
}
 
 
int IODevice::writeData(int address_device, int addr, int nb, unsigned short *tab_reg)
{
    int rc = 0;
    unsigned char counter = 0;
    // 'E' - event work mode, 8 - data bits, 1 - stop bit - serial port configuration
    modbus_t* my_modbus = modbus_new_rtu("/dev/ttyAPP1", 4800, 'E', 8, 1);
    uint32_t old_response_to_sec;
    uint32_t old_response_to_usec;
    modbus_get_response_timeout(my_modbus, &old_response_to_sec, &old_response_to_usec);
    modbus_set_response_timeout(my_modbus, 0, 30000);
 
    if(modbus_connect(my_modbus)  == 0){
 
    modbus_set_slave(my_modbus, address_device);
 
    while(1)
    {
    rc = modbus_write_registers(my_modbus, addr, nb,tab_reg);
    if(rc != -1)
    {
    break;
    }
    counter ++;
    if(counter == 3){
    modbus_close(my_modbus);
    modbus_free(my_modbus);
    return (-1);
    break;
    }
    }
 
    }
    modbus_close(my_modbus);
    modbus_free(my_modbus);
    return(1);
}
 
 
int IODevice::readId(int address_device, int size, unsigned char* text)
{
    int rc = 0;
    unsigned char counter = 0;
    // 'E' - event work mode, 8 - data bits, 1 - stop bit - serial port configuration
    modbus_t* my_modbus = modbus_new_rtu("/dev/ttyAPP1", 4800, 'E', 8, 1);
    uint32_t old_response_to_sec;
    uint32_t old_response_to_usec;
    modbus_get_response_timeout(my_modbus, &old_response_to_sec, &old_response_to_usec);
    modbus_set_response_timeout(my_modbus, 0, 30000);
 
    if(modbus_connect(my_modbus)  == 0){
 
    modbus_set_slave(my_modbus, address_device);
 
    while(1)
    {
    rc = modbus_report_slave_id(my_modbus, size,text);
    if(rc != -1)
    {
    break;
    }
    counter ++;
    if(counter == 3){
    modbus_close(my_modbus);
    modbus_free(my_modbus);
    return (-1);
    break;
    }
    }
 
    }
    modbus_close(my_modbus);
    modbus_free(my_modbus);
    return(1);
}


Проверил работают все функции.
Получается открытие порта ---> настройка порта ---> передача, прием, чтение ID. Все основные задачи выполняет этот класс.
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
02.12.2019, 14:50
Цитата Сообщение от Alex_Golubev Посмотреть сообщение
Вот сделал класс iodevice
Отлично, теперь применяйте его в том классе, что выше него. Также отладьте класс в консоли, чтение одного датчика и чтение всех по очереди.
0
2 / 2 / 2
Регистрация: 27.02.2016
Сообщений: 247
02.12.2019, 16:08  [ТС]
insite2012, Есть замечания по классу который выше написал?
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
02.12.2019, 17:07
Цитата Сообщение от Alex_Golubev Посмотреть сообщение
Есть замечания по классу который выше написал?
В общем нет, поскольку логику работы вашего устройства я не знаю. Один вопрос: зачем возвратом всегда int? Это вы типа в старом стиле, возврат как показатель ошибки идет?
0
2 / 2 / 2
Регистрация: 27.02.2016
Сообщений: 247
02.12.2019, 17:17  [ТС]
Цитата Сообщение от insite2012 Посмотреть сообщение
возврат как показатель ошибки идет
Да.
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
02.12.2019, 18:02
Цитата Сообщение от Alex_Golubev Посмотреть сообщение
Да.
Тогда понятно. Я предпочитаю использовать исключения.
0
2 / 2 / 2
Регистрация: 27.02.2016
Сообщений: 247
02.12.2019, 18:08  [ТС]
Я не понял. Что значит примените в классе что выше него?
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
02.12.2019, 19:38
Цитата Сообщение от Alex_Golubev Посмотреть сообщение
Я не понял. Что значит примените в классе что выше него?
Класс, который уже будет работать (в потоке или нет) с датчиками.
0
2 / 2 / 2
Регистрация: 27.02.2016
Сообщений: 247
03.12.2019, 18:35  [ТС]
Создал три класса.
iodevice открыть порт---> задать параметры порта ---> прочитать, записать, ID
sensorinfo читать,писать определенные данные по простому ручное управление
sensormanager один проход авто режима. Цикл опрос датчиков, управление исходя из их состояния.
Код не приложил там простыни здоровые. Если нужно покажу.

Что делать дальше ?
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
03.12.2019, 20:29
Цитата Сообщение от Alex_Golubev Посмотреть сообщение
Код не приложил там простыни здоровые.
Покажите заголовочный файл sensormanager.h. Без реализации.
0
2 / 2 / 2
Регистрация: 27.02.2016
Сообщений: 247
04.12.2019, 07:22  [ТС]
получается вот:
sensormanager.h
Кликните здесь для просмотра всего текста
C++ (Qt)
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
#ifndef SENSORMANAGER_H
#define SENSORMANAGER_H
 
#include <QObject>
#include <sensorinfo.h>
 
class SensorManager : public QObject
{
    Q_OBJECT
private:
   bool m_runFlag;
   QByteArray deviceInfo;
 
public:
    explicit SensorManager(const QByteArray& deviceInfo, QObject *parent = 0);
 
signals:
    void signalReadStep(quint32 step);
    void signalReadStop();
 
public slots:
    void slotAutoModeBalancer();
    void slotStopBalancer();
};
 
#endif // SENSORMANAGER_H
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
04.12.2019, 10:04
Цитата Сообщение от Alex_Golubev Посмотреть сообщение
получается вот:
В принципе норм.
Добавьте сигнал старта автоматического опроса датчиков и метод опроса одного датчика по адресу (или еще к-либо признаку).
Да, и добавьте сигналы для возврата результатов опроса (единичного или группового) в вызывающий код.
0
2 / 2 / 2
Регистрация: 27.02.2016
Сообщений: 247
04.12.2019, 11:03  [ТС]
Все сделал:
Кликните здесь для просмотра всего текста
C++ (Qt)
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
#ifndef SENSORMANAGER_H
#define SENSORMANAGER_H
 
#include <QObject>
#include <sensorinfo.h>
 
class SensorManager : public QObject
{
    Q_OBJECT
private:
   bool m_runFlag;
   QByteArray deviceInfo;
   int numberBalancer;
 
public:
    explicit SensorManager(const QByteArray& deviceInfo, QObject *parent = 0);
 
    void readOneBalancer(int addressDevice);
 
signals:
    void signalInfoOne(quint32 *buf);
    void signalStartOne(quint32 size);
    void signalStopOne();
 
    void signalInfoAuto(quint32 *buf);
    void signalStartAuto(quint32 size);
    void signalStepAuto(quint32 step);
    void signalStopAuto();
public slots:
    void slotAutoModeBalancer();
    void slotStopBalancer();
};
 
#endif // SENSORMANAGER_H
0
2737 / 892 / 331
Регистрация: 10.02.2018
Сообщений: 2,135
04.12.2019, 14:59
Alex_Golubev, ещё один вариант организации последовательного цикла в отдельном потоке с возможностью прервать его и без STL. В архиве проекты с двумя вариантами.

mainwindow.h
C++ (Qt)
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
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
 
#include <QMainWindow>
#include <QLabel>
#include <QThread>
#include "thread.h"
 
class MainWindow : public QMainWindow
{
    Q_OBJECT
 
public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
 
signals:
    void signalStartOne(int id);
 
public slots:
    void StartOne();
 
    void DoneOne(int id, QByteArray answer);
    void DoneAll(QByteArray answer);
 
protected:
    void timerEvent(QTimerEvent *event) override;
 
protected:
    QLabel* label;
    Thread* thread;
    QThread qthread;
};
 
#endif // MAINWINDOW_H
mainwindow.cpp
C++ (Qt)
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
97
98
99
100
#include "mainwindow.h"
#include <QVBoxLayout>
#include <QPushButton>
#include <QMessageBox>
 
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    thread = new Thread;
    thread->moveToThread(&qthread);
    qthread.start();
 
    QPushButton* btnReadOne = new QPushButton("read one");
    QPushButton* btnReadAll = new QPushButton("read all");
    QPushButton* btnStopRead = new QPushButton("stop read");
    label = new QLabel;
 
    QVBoxLayout* l = new QVBoxLayout();
    l->addWidget(btnReadOne);
    l->addWidget(btnReadAll);
    l->addWidget(btnStopRead);
    l->addWidget(label);
    QWidget* w = new QWidget;
    w->setLayout(l);
    setCentralWidget(w);
 
    connect(btnReadOne, SIGNAL(clicked()), this, SLOT(StartOne()));
    connect(this, SIGNAL(signalStartOne(int)), thread, SLOT(readOne(int)));
    connect(btnReadAll, SIGNAL(clicked()), thread, SLOT(readAll()));
    connect(btnStopRead, SIGNAL(clicked()), thread, SLOT(stopRead()));
 
    connect(thread, SIGNAL(readOneDone(int,QByteArray)), this, SLOT(DoneOne(int,QByteArray)));
    connect(thread, SIGNAL(readAllDone(QByteArray)), this, SLOT(DoneAll(QByteArray)));
 
    startTimer(100);
}
 
MainWindow::~MainWindow()
{
    delete thread;
}
 
void MainWindow::StartOne()
{
    // напрямую нельзя, не предусмотрено в реализации
    //thread->readOne(2);
 
    // только через очередь сообщений
    emit signalStartOne(2);
}
 
void MainWindow::DoneOne(int id, QByteArray answer)
{
    QMessageBox box;
    box.setWindowTitle("Done One");
    box.setText(answer);
    box.exec();
}
 
void MainWindow::DoneAll(QByteArray answer)
{
    QMessageBox box;
    box.setWindowTitle("Done All");
    box.setText(answer);
    box.exec();
}
 
void MainWindow::timerEvent(QTimerEvent *event)
{
    STaskState state = thread->getState();
 
    QString str = "Unknown";
 
    switch (state.current)
    {
    case keTaskNone:
        str= "None";
        break;
 
    case keTaskReadOne:
        str= "ReadOne";
        break;
 
    case keTaskReadAll:
        str= "ReadAll";
        break;
 
    case keTaskStopRead:
        str= "StopRead";
        break;
 
    case keTaskTerminate:
        str= "Terminate";
        break;
    }
 
    str += QString(" (%0/%1)").arg(state.doneCount).arg(state.maxCount);
 
    label->setText(str);
}

thread.h
C++ (Qt)
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
#ifndef THREAD_H
#define THREAD_H
 
#include <QObject>
#include <QMutex>
 
// команды/режимы
enum ETask
{
    keTaskNone,         // ничего не делать
    keTaskReadOne,      // прочесть один датчик
    keTaskReadAll,      // прочесть все датчики
    keTaskStopRead,     // прервать чтение
    keTaskTerminate,    // завершить работу потока
};
 
// состояние
struct STaskState
{
    ETask current;  // текущий режим
    int currentId;  // id опрашиваемого датчика
    int doneCount;  // количество опрошенных датчиков
    int maxCount;   // общее количество датчиков, которые нужно опросить
};
 
// класс опроса датчиков в отдельном потоке
class Thread : public QObject
{
    Q_OBJECT
 
public:
    explicit Thread(QObject *parent = 0);
    ~Thread();
 
    STaskState getState() { QMutexLocker lck(&mutex); return state; } // текущее состояние потока: режим и рогресс
 
public slots:
    void readOne(int id); // начать одиночный опрос датчика
    void readAll(); // начать последовательный опрос всех имеющихся датчиков
    void stopRead(); // прервать опрос
 
signals:
    void readOneDone(int id, QByteArray answer); // испускается при завершении одиночного опроса датчика
    void readAllDone(QByteArray answer); // испускается при завершении последовательного опроса всех датчиков
 
    void signalUpdate(); // испускается изнутри для помещения в очередь команды на выполнение опроса
 
protected slots:
    void update(); // один шаг опроса
 
protected:
    QByteArray execOne(int id); // синхронно опросить датчик
 
protected:
    QMutex mutex;
    STaskState state;
    QByteArray answerAll;
};
 
#endif // THREAD_H
thread.cpp
C++ (Qt)
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include "thread.h"
#include <chrono>
#include <thread>
 
Thread::Thread(QObject *parent) : QObject(parent)
{
    // начальное состояние
    state.current = keTaskNone;
    state.currentId = 0;
    state.doneCount = 0;
    state.maxCount = 0;
 
    // соединяем сигнал и слот через очередь сообщений
    connect(this, SIGNAL(signalUpdate()), this, SLOT(update()), Qt::QueuedConnection);
}
 
Thread::~Thread()
{
}
 
void Thread::readOne(int id)
{
    // блокируем доступ к состоянию
    QMutexLocker lck(&mutex);
 
    // запоминаем команду: одиночный опрос датчика <id>
    state.current = keTaskReadOne;
    state.currentId = id;
    state.doneCount = 0;
    state.maxCount = 1;
 
    // помещаем в очередь команду на обновление
    emit signalUpdate();
}
 
void Thread::readAll()
{
    // блокируем доступ к состоянию
    QMutexLocker lck(&mutex);
 
    // запоминаем команду: последовательный опрос всех датчиков
    state.current = keTaskReadAll;
    state.doneCount = 0;
    state.maxCount = 200;
 
    // помещаем в очередь команду на обновление
    emit signalUpdate();
}
 
void Thread::stopRead()
{
    // блокируем доступ к состоянию
    QMutexLocker lck(&mutex);
 
    // запоминаем команду: прервать опрос
    state.current = keTaskStopRead;
}
 
void Thread::update()
{
    // одиночный опрос датчика
    if (state.current == keTaskReadOne)
    {
        if (state.doneCount < state.maxCount)
        {
            // синхронный опрос датчика
            QByteArray answer = execOne(state.currentId);
 
            // обнавляем статус
            mutex.lock();
            state.doneCount++;
            mutex.unlock();
 
            // испускаем сигнал о завершении
            emit readOneDone(state.currentId, answer);
        }
    }
    // последовательный опрос всех датчиков
    else if (state.current == keTaskReadAll)
    {
        if (state.doneCount < state.maxCount)
        {
            // синхронный опрос датчика
            QByteArray answer = execOne(state.doneCount);
 
            // запоминаем результат
            if (state.doneCount > 0)
                answerAll += "," + answer;
            else
                answerAll = answer;
 
            // обнавляем статус (переходим к следующему датчику)
            mutex.lock();
            state.doneCount++;
            mutex.unlock();
 
            // если все датчики опрошены
            if (state.doneCount >= state.maxCount)
            {
                // испускаем сигнал о завершении
                emit readAllDone(answerAll);
 
                // промежуточные результаты больше не нужны
                answerAll.clear();
            }
            else
            {
                // помещаем в очередь команду на повторное обновление
                emit signalUpdate();
            }
        }
    }
}
 
QByteArray Thread::execOne(int id)
{
    // эмулируем задержку при запросе к датчику
    std::this_thread::sleep_for(std::chrono::milliseconds(40));
 
    // в качестве ответа возвращаем id датчика в текстовом виде
    return QByteArray().setNum(id);
}
Вложения
Тип файла: zip test_thread.zip (9.0 Кб, 1 просмотров)
0
2 / 2 / 2
Регистрация: 27.02.2016
Сообщений: 247
04.12.2019, 16:33  [ТС]
Ygg, Вы через mutex тормозите?

Мне интересно что скажет insite2012.
0
2737 / 892 / 331
Регистрация: 10.02.2018
Сообщений: 2,135
04.12.2019, 18:16
Цитата Сообщение от Alex_Golubev Посмотреть сообщение
Вы через mutex тормозите?
Мьютекс используется для обеспечения целостности структуры STaskState. Потому что доступ к этой структуре осуществляется одновременно как из рабочего потока, так и из интерфейсного.

Добавлено через 1 час 4 минуты
Кликните здесь для просмотра всего текста
Code
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
========================================================================
Последовательный опрос всех датчиков
========================================================================
 
1) начать цикл опроса (контекст QApplication)
интерфейсный поток испускает сигнал <clicked>
испускаемый сигнал поступает в очередь рабочего потока
 
                  |----------------|
                  |                |
                  |                |
                  |                V
                  |       
    очередь       |             очередь
   сообщений      |            сообщений
 +------------+   |          +------------+
 |            |   |          |            |
 +------------+   |          +------------+
 |            |   |          |            |
 +------------+   |          +------------+
 |            |   |          |            |
 +------------+   |          +------------+
 |            |   |          |            |
 +------------+   |          +------------+
       |          |                |
       |          |                |
       V          |                V
+--------------+  |         +--------------+
|              |  |         |              |
| интерфейсный |--|         |   рабочий    |
|    поток     |            |    поток     |
|              |            |              |
| QApplication |            |   QThread    |
|              |            |              |
+--------------+            +--------------+
 
 
2) подготовка (контекст QThread)
сигнал достаётся из очереди и запускается слот <readAll>
слот устанавливает внутренние переменные <state>
слот отсылает сам себе сигнал об обновлении <signalUpdate>
сигнал помещается в конец очереди сообщений
 
                                   |------------|
                                   |            |
                                   |            |
                                   V            |
                                                |
    очередь                     очередь         |
   сообщений                   сообщений        |
 +------------+              +------------+     |
 |            |              |            |     |
 +------------+              +------------+     |
 |            |              |            |     |
 +------------+              +------------+     |
 |            |              |            |     |
 +------------+              +------------+     |
 |            |              |            |     |
 +------------+              +------------+     |
       |                           |            |
       |                           |            |
       V                           V            |
+--------------+            +--------------+    |
|              |            |              |    |
| интерфейсный |            |   рабочий    |    |
|    поток     |            |    поток     |    |
|              |            |              |    |
| QApplication |            |   QThread    |    |
|              |            |              |    |
+--------------+            +--------------+    |
                                   |            |
                                   |            |
                                   |------------|
 
 
3) исполнение одного шага цикла (контекст QThread)
QThread достаёт сигнал из очереди и запускает привязанный слот <update>
слот выполняет опрос одного датчика и увеличивает количество обработанных датчиков
если датчики ещё не закончились, то посылает сам себе сигнал обновления <signalUpdate> (шаг 3 повторяется)
если датчики закончились, то посылает сигнал об окончании интерфейсному потоку <readAllDone> (шаг 4)
 
                                   |------------|
                                   |            |
                                   |            |
                                   V            |
                                                |
    очередь                     очередь         |
   сообщений                   сообщений        |
 +------------+              +------------+     |
 |            |              |            |     |
 +------------+              +------------+     |
 |            |              |            |     |
 +------------+              +------------+     |
 |            |              |            |     |
 +------------+              +------------+     |
 |            |              |            |     |
 +------------+              +------------+     |
       |                           |            |
       |                           |            |
       V                           V            |
+--------------+            +--------------+    |
|              |            |              |    |
| интерфейсный |            |   рабочий    |    |
|    поток     |            |    поток     |    |
|              |            |              |    |
| QApplication |            |   QThread    |    |
|              |            |              |    |
+--------------+            +--------------+    |
                                   |            |
                                   |            |
                                   |------------|
 
 
4) завершение обработки
контекст QThread:
 испускаемый сигнал <readAllDone> поступает в интерфейсную очередь
контекст QApplication:
 сигнал достаётся из очереди и запускается привязанный к нему слот <DoneAll>
 слот выводит на экран отладочное сообщение
 
       ...................
       :                 :
       V                 :
                         :
    очередь              :      очередь
   сообщений             :     сообщений
 +------------+          :   +------------+
 |            |          :   |            |
 +------------+          :   +------------+
 |            |          :   |            |
 +------------+          :   +------------+
 |            |          :   |            |
 +------------+          :   +------------+
 |            |          :   |            |
 +------------+          :   +------------+
       |                 :          |
       |                 :          |
       V                 :          V
+--------------+         :  +--------------+
|              |         :  |              |
| интерфейсный |         :  |   рабочий    |
|    поток     |         :..|    поток     |
|              |            |              |
| QApplication |            |   QThread    |
|              |            |              |
+--------------+            +--------------+
 
 
 
========================================================================
Прервать опрос
========================================================================
 
1) испускается сигнал стоп (контекст QApplication)
интерфейсный поток испускает сигнал <clicked>
испускаемый сигнал поступает в очередь рабочего потока
 
                  |----------------|
                  |                |
                  |                |
                  |                V
                  |       
    очередь       |             очередь
   сообщений      |            сообщений
 +------------+   |          +------------+
 |            |   |          |            |
 +------------+   |          +------------+
 |            |   |          |            |
 +------------+   |          +------------+
 |            |   |          |            |
 +------------+   |          +------------+
 |            |   |          |            |
 +------------+   |          +------------+
       |          |                |
       |          |                |
       V          |                V
+--------------+  |         +--------------+
|              |  |         |              |
| интерфейсный |--|         |   рабочий    |
|    поток     |            |    поток     |
|              |            |              |
| QApplication |            |   QThread    |
|              |            |              |
+--------------+            +--------------+
 
2) прерывание цикла опроса (контекст QThread)
предположим в очереди уже лежит update и поэтому stop ляжет за ним
поток обработает ещё один датчик и положит в конец очереди ещё один update
поток обработает stop и пометит в статусной структуре факт остановки
поток получит ещё один update, но определит что статус уже изменён
опроса датчика не произойдёт, новый update генерироваться не будет
цикл опроса прерван, очередь пуста и поток ждёт команд от интерфейса
 
                                                     ...........
                                                     :         :
                                                     :         :
                                                     V         :
    очередь                     очередь                        :
   сообщений                   сообщений                       :
 +------------+              +------------+    ..............  :  ..............    ..............
 |            |              | stop       |    : update     :  :  : update     :    :            :
 +------------+              +------------+    :............:  :  :............:    :............:
 |            |              | update     |    : stop       :  :  :            :    :            :
 +------------+              +------------+    :............:  :  :............:    :............:
 |            |              |            |    :            :  :  :            :    :            :
 +------------+              +------------+    :............:  :  :............:    :............:
 |            |              |            |    :            :  :  :            :    :            :
 +------------+              +------------+    :............:  :  :............:    :............:
       |                           |                 :         :         :
       |                           |                 :         :         :
       V                           V                 V         :         X
+--------------+            +--------------+                   :
|              |            |              |                   :
| интерфейсный |            |   рабочий    |                   :
|    поток     |            |    поток     |                   :
|              |            |              |                   :
| QApplication |            |   QThread    |                   :
|              |            |              |                   :
+--------------+            +--------------+                   :
                                   :                           :
                                   :...........................:
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
04.12.2019, 21:56
Цитата Сообщение от Alex_Golubev Посмотреть сообщение
Мне интересно что скажет insite2012.
А что тут сказать. Я так понимаю, что оба класса у вас отлажены в консоли и работают именно так, как и задумано, выполняют все, что и должны?
Если все так, то теперь можно начинать строить интерфейс. Пока без использования этих классов, просто внешний вид. Подумайте, как он должен выглядеть, если в голове трудно все спроектировать-нарисуйте на бумаге внешний вид. И да, учитывайте момент, что ваше приложение должно быть более-менее стандартным, то есть постарайтесь как можно меньше использовать нестандартные и не очевидные для пользователей решения и приемы.
Можете накидать на бумаге и выложить сюда внешний вид, я сделаю примерный макет интерфейса, а вы его потом доведете до ума.
0
2 / 2 / 2
Регистрация: 27.02.2016
Сообщений: 247
05.12.2019, 01:28  [ТС]
Интерфейс. Я уже сделал могу показать. И классы написал. Мне нужно понять как работать с потоками. Остонавливатся поток из другого диалогового окна.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
05.12.2019, 01:28

Главный поток и вторичный
1. При нажатии на кнопку Start в новом потоке запускается server.AcceptTcpClient() (который возвращает обьект TcpClient соединенного...

Каким синтаксисом запускается вторичный поток?
Каким синтаксисом запускается вторичный поток?

Не получается с вторичного потока обратиться напрямую к объекту из главного потока
Добрый день. Проблема заключается в том что не могу в WPF с вторичного потока обратится на прямую к объекту из главного потока. WPF пока...

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

UiThread, как вынести процесс из главного потока?
Я - новичок в Android. Нужно в этом куске кода реализовать многопоточность. Очистить главный поток от операций, связанных с соединением с...


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

Или воспользуйтесь поиском по форуму:
100
Ответ Создать тему
Новые блоги и статьи
мат медиц модель 30. презентация проекта
anaschu 27.08.2026
хоп хоп хоп хидахоп, а я кладую))
Как у меня протекала болезнь
zorxor 27.08.2026
Здравствуйте, друзья! Эта запись блога предназначена именно для вас - для моих дорогих друзей, которые знали меня лично. Чтобы ответить на вопрос - а что же со мной произошло на самом деле? Я учился. . .
Нашел вот забавное видео о измерениях. Лучшее что я видел на эту тему
kumehtar 26.08.2026
ILETXiw9bMQ Основная суть и тезисы по измерениям: 0D (Нулевое измерение): точка, не имеющая длины, ширины, высоты или объема. Объект не может перемещаться в 0D. 1D (Первое измерение):. . .
[EasyBuilder Pro] Памятка по разработке для панелей Weintek
ФедосеевПавел 26.08.2026
Памятка по разработке для панелей Weintek ВВЕДЕНИЕ Ранее, при реализации проектов основное внимание уделял разработке управляющей программы для контроллера, а панели оператора доставалось время. . .
Модель по догадкам
anaschu 25.08.2026
Прошло две недели. Я уже рассказывал, как разговаривал с сотрудниками у сортировки и как понял, что главная ветка — не про приёмку, а про отбор. Но тогда я думал, что понял механику. На этой неделе я. . .
Запись в регистр сведений независимо от заполненности табличной части
Maks 25.08.2026
Реализация из решения ниже выполнена на нетиповом документе с несколькими табличными частями, разработанного в КА2. Задача: Обеспечить запись документа в регистр сведений независимо от. . .
Ноутбук Альфария
kumehtar 24.08.2026
Встретился тут в сети ноутбук Альфария, примарха Альфа-Легиона. Хотя возможно, это ноутбук Омегона, разумеется. Ну как вам?
Мастера простых решений
DevAlt 23.08.2026
В сишарп стэках winforms, да и wpf существует сложная система связывания источниках данных и элементов формы(текстовые поля и метки), опирается все это на технологию событий и мета. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru