C++ 非阻塞工作进程-中断文件复制
我处理的文件非常大,超过数百GB。用户需要能够在磁盘之间移动这些文件,并且位于没有默认文件管理器的受限系统上。用户可能会意识到他们犯了错误并取消操作,据我所知,用户将不得不等待当前的复制或重命名操作完成。这会让他们感到沮丧,因为他们可能会等上几分钟,结果却发现他们的多GB文件仍然被复制。在复制的情况下,我可以删除第二个文件,但是在重命名的情况下,我用它来移动文件,我必须反向重复这个操作来撤销它,这是不可接受的C++ 非阻塞工作进程-中断文件复制,c++,qt,qt4.8,qfile,C++,Qt,Qt4.8,Qfile,我处理的文件非常大,超过数百GB。用户需要能够在磁盘之间移动这些文件,并且位于没有默认文件管理器的受限系统上。用户可能会意识到他们犯了错误并取消操作,据我所知,用户将不得不等待当前的复制或重命名操作完成。这会让他们感到沮丧,因为他们可能会等上几分钟,结果却发现他们的多GB文件仍然被复制。在复制的情况下,我可以删除第二个文件,但是在重命名的情况下,我用它来移动文件,我必须反向重复这个操作来撤销它,这是不可接受的 有没有什么方法可以中断我在QFile文档中没有看到的copy()和rename(),或
有没有什么方法可以中断我在QFile文档中没有看到的copy()和rename(),或者我需要创建自己的类来处理copy和rename?我认为您正在寻找的函数不存在 您可以做的是,不使用copy()函数,而是创建一个新文件,并从旧文件中逐步读取(qint64 maxSize)QByteArray,然后将(const QByteArray&byteArray)写入新文件。
通过这种方式,您可以自己控制流量,只需检查用户在每次读/写之间是否没有按“取消”。我认为文件大小对重命名所需的时间没有任何影响 因为copy-Qt没有内置任何东西,所以您必须自己实现它。这里的关键问题是,你必须找到一种方法来不断地投票取消拷贝。这意味着您无法锁定主线程以处理事件 无论您是为了让主线程保持响应而选择额外的线程,还是决定使用主线程—在这两种情况下,您都需要实现“分段”复制—使用缓冲区一次复制一个块,直到文件被复制或复制被取消。您需要这样才能处理用户事件并跟踪复制进度 我建议您实现一个
QObject
派生的copy helper worker类,它跟踪文件名、总大小、缓冲区大小、进度和取消时的清理。然后,您可以选择是在主线程中使用它,还是在专用线程中使用它
编辑:找到了,但您最好仔细检查一下,因为它是作为一个示例完成的,尚未经过彻底测试:
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;
};
类CopyHelper:公共QObject{
Q_对象
Q_属性(qreal progress READ progress WRITE setProgress NOTIFY progressChanged)
公众:
CopyHelper(QString sPath、QString dPath、quint64 bSize=1024*1024):
isCancelled(false)、bufferSize(bSize)、prog(0.0)、源(sPath)、目标(dPath)、位置(0){
~CopyHelper(){free(buff);}
qreal progress()常量{return prog;}
无效设置进度(QRP){
如果(p!=prog){
prog=p;
发生了变化();
}
}
公众时段:
void begin(){
如果(!source.open(QIODevice::ReadOnly)){
qDebug()我很害怕。我宁愿花几个小时在其他地方。我会用你建议的模式做一些事情,它必须在另一个线程中,以确保GUI可以随进度更新。好的一面是,我可以通过这种方式给出特定于文件的进度。至于rename(),我用它来移动文件。当你移动到同一磁盘上的另一个目录时,它是不变的,但从一个磁盘到另一个磁盘确实需要时间,这取决于文件大小。@MildWolfie-我想我已经做过类似的事情了,几小时后回到家时会检查。这就是我为了保持UI响应而做的事情nsive.@MildWolfie-找到它,可能会为您节省一些时间。