Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: gladsky от Июнь 06, 2017, 13:03



Название: Асинхронное копирование файлов вешает интерфейс
Отправлено: gladsky от Июнь 06, 2017, 13:03
Копирование большого файла в основном потоке приложения замораживает его интерфейс. Копирование в отдельном потоке не помогает. Подскажите, пожалуйста, почему интерфейс всё равно замерзает.
1. QtConcurrent::run. Начал с простого.
Выделяю копирование в отдельную функцию
Код:
void MainWindow::fileCopy(const QString &source, const QString &destination)
{
    QFile::copy(source, destination);
}
Само копирование:
Код:
QString source = "D:\Distrib\rlink20022014.rar";
QString destination = "\\Distrib\rlink20022014.rar"; //копирую в сетевую папку
QFuture<void> f1 = QtConcurrent::run(this, &MainWindow::fileCopy, source, destination);
При копировании большого файла интерфейс подвисает :(.

2. Ладно, идём дальше, беру QThread
Сначала делаю класс для копирования
filecopy.h
Код:
#ifndef FILECOPY_H
#define FILECOPY_H

#include <QObject>

class FileCopy : public QObject
{
    Q_OBJECT
public:
    FileCopy(const QStringList &files, const QString &path, QObject *parent = 0);
    ~FileCopy();

signals:
    void finished();

private slots:
    void process();

private:
    QStringList filesList;
    QString destination;
};

#endif // FILECOPY_H
filecopy.cpp
Код:
#include "filecopy.h"
#include <QFileInfo>
#include <QDir>
#include <QDebug>

FileCopy::FileCopy(const QStringList &files, const QString &path, QObject *parent)
     : QObject(parent)
{
    this->filesList = files;
    this->destination = path;
}

FileCopy::~FileCopy()
{

}

void FileCopy::process()
{
    foreach (QString file, filesList) {
        QFileInfo file_info(file);
        QString file_name = file_info.fileName();
        QString destination_file = destination + QDir::separator() + file_name;
        QFile::copy(file, destination_file);
        emit finished();
    }
}
Копируем:
Код:
        QStringList file_list;
        file_list << QDir::toNativeSeparators(url.toLocalFile());
        QString copyToAddress = "\\Distrib"
        QThread *thread = new QThread();
        FileCopy *fileCopy = new FileCopy(file_list, copyToAddress);
        fileCopy->moveToThread(thread);
        connect(thread, SIGNAL(started()), fileCopy, SLOT(process()));
        connect(fileCopy, SIGNAL(finished()), thread, SLOT(quit()));
        connect(fileCopy, SIGNAL(finished()), fileCopy, SLOT(deleteLater()));
        connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
        thread->start();
Не помогло  >:(

3. Может виновата QFile::copy? Переписываю класс filecopy, избавляюсь от QFile::copy
filecopy.h
Код:
#ifndef FILECOPY_H
#define FILECOPY_H

#include <QObject>

class FileCopy : public QObject
{
    Q_OBJECT
public:
    explicit FileCopy(const QStringList &files, const QString &path, QObject *parent = 0);
    ~FileCopy();

signals:
    void finished();

private slots:
    void process();

private:
    QStringList filesList;
    QString destination;
    quint64 bufferSize;
    char *buffer;
};

#endif // FILECOPY_H
filecopy.cpp
Код:
#include "filecopy.h"
#include <QFileInfo>
#include <QDir>
#include <QDebug>

FileCopy::FileCopy(const QStringList &files, const QString &path, QObject *parent)
     : filesList(files), destination(path), bufferSize(1024*512), QObject(parent)
{ }

FileCopy::~FileCopy()
{ }

void FileCopy::process()
{
    foreach (QString file, filesList) {

        QFileInfo fileInfo(file);
        QString sFileName = fileInfo.fileName();
        QString sDestinationFile = destination + QDir::separator() + sFileName;
        int i = 1;
        while (QFile::exists(sDestinationFile)) {
            sFileName = fileInfo.fileName() + "_" + QString::number(i)
                    + "." + fileInfo.suffix();
            sDestinationFile = destination + QDir::separator() + sFileName;
            i++;
        }

        QFile sourceFile(file);
        QFile destinationFile(sDestinationFile);
        if (!sourceFile.open(QIODevice::ReadOnly)) {
            emit finished();
            return;
        }
        if (!destinationFile.open(QIODevice::WriteOnly)) {
            emit finished();
            return;
        }
        quint64 fileSize = sourceFile.size();
        if (!destinationFile.resize(fileSize)) {
            emit finished();
            return;
        }
        buffer = (char*)malloc(bufferSize);
        if(!buffer) {
            emit finished();
            return;
        }

        quint64 position = 0;
        while (position < fileSize) {
            quint64 chunk = fileSize - position;
            quint64 i = chunk > bufferSize ? bufferSize : chunk;
            sourceFile.read(buffer, i);
            destinationFile.write(buffer, i);
            position += i;
            sourceFile.seek(position);
            destinationFile.seek(position);
        }

        emit finished();
    }
}
В основной программе копирование не изменилось. Чтобы не ходить далеко, привожу ещё раз
Код:
        QStringList file_list;
        file_list << QDir::toNativeSeparators(url.toLocalFile());
        QString copyToAddress = "\\Distrib"
        QThread *thread = new QThread();
        FileCopy *fileCopy = new FileCopy(file_list, copyToAddress);
        fileCopy->moveToThread(thread);
        connect(thread, SIGNAL(started()), fileCopy, SLOT(process()));
        connect(fileCopy, SIGNAL(finished()), thread, SLOT(quit()));
        connect(fileCopy, SIGNAL(finished()), fileCopy, SLOT(deleteLater()));
        connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
        thread->start();
Оно, конечно, теперь не виснет намертво на всё время копирования, но реагирует рывками - чем больше буффер, тем на большее время подвисает.


Название: Re: Асинхронное копирование файлов вешает и&#
Отправлено: Пантер от Июнь 06, 2017, 13:17
Странно, первый и второй способы корректны. А у тебя случаем не фризится вся система?


Название: Re: Асинхронное копирование файлов вешает интерфейс
Отправлено: Пантер от Июнь 06, 2017, 13:27
И еще вопрос - ты привел полный код или упростил его, удалив ненужное? Может, проблема в том коде, который ты не приводишь? Допустим, пытаешься GUI дергать внутри функции копирования.


Название: Re: Асинхронное копирование файлов вешает интерфейс
Отправлено: gladsky от Июнь 06, 2017, 14:03
И еще вопрос - ты привел полный код или упростил его, удалив ненужное? Может, проблема в том коде, который ты не приводишь? Допустим, пытаешься GUI дергать внутри функции копирования.

Система не подвисает.
Код только копирует файлы, больше ничего ещё не навесил. Если заменить копирование на что-нибудь простое, типа как код ниже, то отрабатывает нормально, интерфейс не фризится.
Код:
for (int i = 0; i < 100; i++) {
    qDebug() << i;
    Sleep(uint(5000));
}


Название: Re: Асинхронное копирование файлов вешает интерфейс
Отправлено: Пантер от Июнь 06, 2017, 14:14
А под замораживанием интерфейса ты что понимаешь?
Я писал свой ФМ, проблем таких не возникало.


Название: Re: Асинхронное копирование файлов вешает интерфейс
Отправлено: qate от Июнь 07, 2017, 08:18
сделал бы минимальный проект, можно было бы посмотреть
а разбираться в портянках кода и копипастить их откровенно лень


Название: Re: Асинхронное копирование файлов вешает интерфейс
Отправлено: Авварон от Июнь 07, 2017, 13:21
+1, первый способ должен рабоатть. Случаем, футуру нигде не ждут?