c++ - 非阻塞 worker - 中断文件复制

标签 c++ qt qt4.8 qfile

我正在处理非常大的文件,大小超过数百 GB。用户需要能够在磁盘之间移动这些文件,并且在没有默认文件管理器的受限系统上。用户有可能意识到他们犯了错误并取消操作,据我所知,用户将不得不等待当前的复制或重命名操作完成。这可能会让他们感到沮丧,因为他们可能会等待几分钟,却发现他们的许多 GB 文件仍然被复制。在复制的情况下,我可以删除第二个文件,但在我用来移动文件的重命名的情况下,我必须反向重复操作以撤消它,这是 Not Acceptable 。

有没有什么方法可以中断我在 QFile 的文档中没有看到的 copy() 和 rename(),或者我是否需要将我自己的类放在一起来处理复制和重命名?

最佳答案

我认为文件大小对重命名所需的时间没有任何影响。

对于拷贝 - Qt 没有提供任何内置功能,您必须自己实现它。这里的关键问题是你必须找到一些方法来连续轮询拷贝取消。这意味着您不能为了能够处理事件而锁定主线程。

无论您是选择一个额外的线程来保持主线程的响应,还是决定使用主线程——在这两种情况下,您都需要实现“碎片化”复制——使用缓冲区一次一个 block ,直到文件被复制或复制被取消。您需要它才能处理用户事件并跟踪复制进度。

我建议您实现一个 QObject 派生的复制助手工作类,它跟踪文件名、总大小、缓冲区大小、进度并在取消时进行清理。然后是选择在主线程还是在专用线程中使用它。

编辑:找到了,但你最好仔细检查一下,因为它是作为一个例子完成的,还没有经过彻底测试:

class CopyHelper : public QObject {
    Q_OBJECT
    Q_PROPERTY(qreal progress READ progress WRITE setProgress NOTIFY progressChanged)
public:
    CopyHelper(QString sPath, QString dPath, quint64 bSize = 1024 * 1024) :
        isCancelled(false), bufferSize(bSize), prog(0.0), source(sPath), destination(dPath), position(0) { }
    ~CopyHelper() { free(buff); }

    qreal progress() const { return prog; }
    void setProgress(qreal p) {
        if (p != prog) {
            prog = p;
            emit progressChanged();
        }
    }

public slots:
    void begin() {
        if (!source.open(QIODevice::ReadOnly)) {
            qDebug() << "could not open source, aborting";
            emit done();
            return;
        }
        fileSize = source.size();
        if (!destination.open(QIODevice::WriteOnly)) {
            qDebug() << "could not open destination, aborting";
            // maybe check for overwriting and ask to proceed
            emit done();
            return;
        }
        if (!destination.resize(fileSize)) {
            qDebug() << "could not resize, aborting";
            emit done();
            return;
        }
        buff = (char*)malloc(bufferSize);
        if (!buff) {
            qDebug() << "could not allocate buffer, aborting";
            emit done();
            return;
        }
        QMetaObject::invokeMethod(this, "step", Qt::QueuedConnection);
        //timer.start();
    }
    void step() {
        if (!isCancelled) {
            if (position < fileSize) {
                quint64 chunk = fileSize - position;
                quint64 l = chunk > bufferSize ? bufferSize : chunk;
                source.read(buff, l);
                destination.write(buff, l);
                position += l;
                source.seek(position);
                destination.seek(position);
                setProgress((qreal)position / fileSize);
                //std::this_thread::sleep_for(std::chrono::milliseconds(100)); // for testing
                QMetaObject::invokeMethod(this, "step", Qt::QueuedConnection);
            } else {
                //qDebug() << timer.elapsed();
                emit done();
                return;
            }
        } else {
            if (!destination.remove()) qDebug() << "delete failed";
            emit done();
        }
    }
    void cancel() { isCancelled = true; }

signals:
    void progressChanged();
    void done();

private:
    bool isCancelled;
    quint64 bufferSize;
    qreal prog;
    QFile source, destination;
    quint64 fileSize, position;
    char * buff;
    //QElapsedTimer timer;
};

done() 信号用于deleteLater() 复制助手/关闭复制对话框等。您可以启用经过计时器并使用它来实现耗时属性和估计时间。暂停是另一个可能要实现的功能。使用 QMetaObject::invokeMethod() 允许事件循环定期处理用户事件,因此您可以取消和更新进度,进度从 0 到 1。您也可以轻松调整它以移动文件。

关于c++ - 非阻塞 worker - 中断文件复制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32952474/

相关文章:

c++ - QMainWindow 中的 QT 4.8 填充

qt - QApplication段错误

drag-and-drop - 如何拖放自定义小部件?

c++ - 如何在 OpenCV 中编辑/更新 YAML 文件?

c++ - 将函数/构造函数参数限制为特定类型的成员

c++ - 分割大量文本数据的算法

c++ - XOR 加密算法无法处理长度超过 127 个字符的输入

c++ - 在检查边界的同时通过 vector 运行光标

c++ - 为什么我不能使用继承来实现C++中的接口(interface)?

c++ - 使用 Wayland 在 Linux 上获取 Capslock 状态