Russian Qt Forum
Октябрь 04, 2024, 20:16 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Высылка сигнала из потока  (Прочитано 10693 раз)
markie
Гость
« : Февраль 20, 2013, 20:59 »

Всем привет! Не могу понять, почему сигналы из потока (не основного) не высылаются на каждой итерации цикла? Например, есть следующий код:
Код:
while (1)
{
    ....
    emit progress();
    ....
}

И сигналы progress, которые должны были высылаться на каждой итерации, доходят до основного потока только после выхода из цикла. Может быть кто-нибудь знает, с чем это может быть связано?

P.S. Код выноситься в отдельный поток с помощью moveToThread, наследования от QThread нет.
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #1 : Февраль 20, 2013, 21:27 »

Threads and QObjects, в частности "Signals and Slots Across Threads". Чтобы основной поток обработал события, после emit progress() нужно вызвать QCoreApplication::processEvents. Только надо подумать, надо ли так часто основной поток дергать.
Записан

Пока сам не сделаешь...
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #2 : Февраль 21, 2013, 12:43 »

Threads and QObjects, в частности "Signals and Slots Across Threads". Чтобы основной поток обработал события, после emit progress() нужно вызвать QCoreApplication::processEvents. Только надо подумать, надо ли так часто основной поток дергать.
По-моему QCoreApplication::processEvents будет извлекать события из EventLoop текущего потока. А если главная нитка висит на своем EventLoop, то зачем еще processEvents?

Вот если по каким-то причинам while крутится в главной нитке - получится именно описанный эффект. Это легко проверить напечатав QThread::currentThreadId в главной нитке и while
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #3 : Февраль 21, 2013, 13:13 »

По-моему QCoreApplication::processEvents будет извлекать события из EventLoop текущего потока. А если главная нитка висит на своем EventLoop, то зачем еще processEvents?
Мы не знаем, что там в каком потоке происходит, и какими сигналами связаны. Вот пусть автор темы и разберется как работают потоки, как между ними сообщения проходят, и что делать, если "сигналы доходят до основного потока только после выхода из цикла".

Судя по
Цитировать
P.S. Код выноситься в отдельный поток с помощью moveToThread, наследования от QThread нет.
его еще много интересных открытий ожидает Улыбающийся.
Записан

Пока сам не сделаешь...
twp
Гость
« Ответ #4 : Февраль 21, 2013, 14:54 »

И сигналы progress, которые должны были высылаться на каждой итерации, доходят до основного потока только после выхода из цикла. Может быть кто-нибудь знает, с чем это может быть связано?

P.S. Код выноситься в отдельный поток с помощью moveToThread, наследования от QThread нет.

Цитата: Assistant
void QThread::run () [virtual protected]
The starting point for the thread. After calling start(), the newly created thread calls this function. The default implementation simply calls exec().
В exec() происходит отсылка сигналов. То получается что цикл крутится, а сигналы накапливаются в очереди. Как только цикл завершается, exec() отрабатывает и заваливает сигналами основной поток Улыбающийся
Записан
markie
Гость
« Ответ #5 : Февраль 21, 2013, 15:03 »

QCoreApplication::processEvents - это какой-то грязный хак в моей ситуации.

Что нужно сделать: из потока, который занимается какой-либо длительной операцией, нужно управлять прогрессбаром.
Соответственно, первое, что пришло в голову - высылка сигнала из потока.

Может быть, кто-нибудь знает каким ещё способом можно организовать то, что требуется?
P.S. На форуме нашёл только это -  http://www.prog.org.ru/topic_23475_0.html. Там упомянули торрент-клиент из примеров Qt, но я так и не смог полностью понять, как а нём устроено обновление прогрессбара.
Записан
Странник
Гость
« Ответ #6 : Февраль 21, 2013, 15:16 »

В exec() происходит отсылка сигналов. То получается что цикл крутится, а сигналы накапливаются в очереди. Как только цикл завершается, exec() отрабатывает и заваливает сигналами основной поток Улыбающийся
в exec() происходит обработка входящих сигналов и вызов слотов. исходщие сигналы на него не завязаны, насколько я помню. кроме того, у автора exec() вызывается до запуска цикла в работу, если он верно использует moveToThread.

вообще - минимальный пример в студию, чтобы мы тут не фантазировали.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #7 : Февраль 21, 2013, 15:22 »

Что нужно сделать: из потока, который занимается какой-либо длительной операцией, нужно управлять прогрессбаром.
Соответственно, первое, что пришло в голову - высылка сигнала из потока.
Да, это и есть самое простое и правильное - если у Вас действительно while в другой (не главной) нитке  Улыбающийся
Записан
markie
Гость
« Ответ #8 : Февраль 21, 2013, 15:54 »

Попробую выложить минимальный пример.

Client - главный поток. Создание треда:
Код:
void Client::createThread(LongAction *action)
{
    QThread *newThread = new QThread(this);
    action->moveToThread(newThread);

    connect(action, SIGNAL(setRange(int, int)),         SLOT(setRangeForProgressBar(int, int)));
    connect(action, SIGNAL(progress(int)),              SLOT(setValueForProgressBar(int)));
    connect(action, SIGNAL(addToLog(QString, bool)),    SLOT(addLog(QString, bool)));
    connect(action, SIGNAL(finished(QString)),          SLOT(structureDownloadFinished(QString)));

    newThread->connect(action, SIGNAL( finished() ), SLOT( quit() ));
    newThread->start();
}

Вот ф-ция, в которой происходит скачка файла. Класс DownloadAction наследуется от LongAction (А LongAction от QObject).
Код:
void DownloadAction::readResponse()
{
    QTcpSocket *clientSocket = (QTcpSocket*)sender();
    QDataStream in(clientSocket);

    for (;;)
    {
        if (!_nextBlockSize)
        {
            if (clientSocket->bytesAvailable() < sizeof(quint32))
                break;

            in >> _nextBlockSize;
        }

        if (clientSocket->bytesAvailable() < _nextBlockSize)
            break;

        if (!_responseCode)
        {
            in >> _responseCode;

            QString info;
            QString fileName;
            int range = 0;

            if (_responseCode == 150)
            {
                in >> fileName >> _fileSize >> range;

                emit setRange(_rowProgressBar, range);

                if (!QFile::exists("download_files"))
                {
                    QDir dir(QDir::currentPath());
                    dir.mkdir("download_files");
                }

                QString filePath = QDir::currentPath() + "/download_files/" + fileName;
                _receiveFile->setFileName(filePath);

                if (!_receiveFile->open(QFile::WriteOnly))
                {
                    emit addToLog("Error open file " + _receiveFile->fileName() + " : " + _receiveFile->errorString(), true);
                    return;
                }

                _nextBlockSize = 0;
                continue;
            }
            else if (_responseCode == 450)
            {
                in >> info;
                continue;
            }

            QString response = "Response: " + QString::number(_responseCode);
            if (!info.isEmpty())
                response += ": " + info;

            emit addToLog(response);
        }

        char data[_nextBlockSize];
        int outData = in.readRawData(data, sizeof(char) * _nextBlockSize);
        _receiveFile->write(data, sizeof(char) * outData);
        _receivingBytes += outData;

        emit progress(_rowProgressBar);
        _nextBlockSize = 0;

        if (_receivingBytes == _fileSize)
        {
            emit addToLog("File " + _receiveFile->fileName() + " received");
            _receiveFile->close();
            emit finished();

            return;
        }
    }
}

readResponse() - как уже наверно все поняли слот, который подвешен на сигнал readyRead().
_rowProgressBar - номер строки в QTableWidget (в нём на каждой строчке располагаются прогрессбары).

P.S. сократил как мог, если что-то понадобится для наилучшего понимания, то пишите.
« Последнее редактирование: Февраль 21, 2013, 18:44 от markie » Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #9 : Февраль 21, 2013, 16:29 »

Client::createThread и DownloadAction::readResponse где вызываются? Вы уверены, что код фактически выполняется в тех потоках, в которых Вы задумывали? Выше уже предлагали проверить с помощью QThread::currentThreadId.
Записан

Пока сам не сделаешь...
markie
Гость
« Ответ #10 : Февраль 21, 2013, 18:53 »

Client - главный поток. currentThreadId() в слоте readResponse и в объекте Client разный.
« Последнее редактирование: Февраль 21, 2013, 18:55 от markie » Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #11 : Февраль 21, 2013, 19:28 »

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

Цитировать
_rowProgressBar - номер строки в QTableWidget (в нём на каждой строчке располагаются прогрессбары).
А этот прогрессбар откуда данные для текущей позиции берет? Можно ли сразу в emit progress() передать текущую позицию? Тогда и action'у не надо знать, какой там прогрессбар к нему будет подключаться, и прогрессбару без разницы, кто ему текущую позицию сообщать будет. Можно еще выяснить, как часто будет вызываться emit progress(), и надо ли с такой частотой прогрессбар обновлять.

И, если можно, такой оффтопный интимный вопрос: до знакомства с Qt, на каких языках писали, с какими фреймворками работали? Улыбающийся
Записан

Пока сам не сделаешь...
markie
Гость
« Ответ #12 : Февраль 21, 2013, 21:44 »

>А этот прогрессбар откуда данные для текущей позиции берет?
Вызывается метод value и инкрементируется текущее значение.

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

>на каких языках писали, с какими фреймворками работали?
C, C++ . Совсем чуть-чуть работал с mfc, вот сейчас кьют.
Записан
Old
Джедай : наставник для всех
*******
Online Online

Сообщений: 4350



Просмотр профиля
« Ответ #13 : Февраль 21, 2013, 22:07 »

2 markie: Скажите, а что пишется в консоль при выполнении вашей программы?

Смущает меня эта строка:
Код
C++ (Qt)
newThread->connect(action, SIGNAL( finished() ), SLOT( quit() ));

Если вы хотите, что бы нить завершалась, при завершении загрузки, то нужно так:
Код
C++ (Qt)
connect(action, SIGNAL( finished() ), newThread, SLOT( quit() ));
Записан
markie
Гость
« Ответ #14 : Февраль 22, 2013, 01:46 »

2Old: эти 2 записи эквивалентны, соответственно в консоль ошибок не выдаётся.
« Последнее редактирование: Февраль 22, 2013, 01:48 от markie » Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.157 секунд. Запросов: 23.