【问题标题】:How to clear QFuture results in QFutureWatcher如何在 QFutureWatcher 中清除 QFuture 结果
【发布时间】:2017-05-04 14:57:40
【问题描述】:

我尝试一次阻止多个文件,然后将它们复制到另一个位置。

应同时阻止源文件和目标文件。因此我不能使用static QFile::copy() 函数。

为了保存和移动我使用QSharedPointer< QFile > 的文件,因为QFile 既不可复制也不可移动。

要完全执行整个操作,我使用QtConcurrent 框架。即:QtConcurrent::mappedReducedQFutureWatcher

要打开所有文件对,我使用 map 仿函数,然后按顺序复制它们,我使用 reduce 仿函数。

using PFile = QSharedPointer< QFile >;

using PFileList = QList< PFile >;

template< typename Result, typename Functor >
struct FunctorWithResultType
        : std::decay_t< Functor >
{

    using result_type = Result;

    FunctorWithResultType(Functor & functor)
        : std::decay_t< Functor >{std::forward< Functor >(functor)}
    { ; }

};

template< typename Result, typename Functor >
FunctorWithResultType< Result, Functor >
addResultType(Functor && functor)
{
    return {functor};
}

class Test
{
public :

    QDir currentDirectory;
    QFutureWatcher< PFileList > fileCopyFutureWatcher;
    // ...
};

// ...

Test::Test()
{
    auto onFilesCopied = [&]
    {
        PFileList copiedFiles = fileCopyFutureWatcher.result();
        qCInfo(usbDevice) << "Files copy operation from drive finished.";
        qCInfo(usbDevice) << copiedFiles.size();
        for (PFile const & file : copiedFiles) {
            //file->close(); // I want copiedFiles to be closed automatically at the end of current scope
        }
    };
    connect(&fileCopyFutureWatcher, &fileCopyFutureWatcher.finished, onFilesCopied);
}

// ...

void Test::onDeviceAdded(QString deviceName)
{
    qCInfo(usbDevice) << "USB device" << deviceName << "is added.";
    if (!fileCopyFutureWatcher.isFinished()) {
        return;
    }
    QDir sourceDirectory{deviceName};
    if (!sourceDirectory.cd("dir")) {
        qCInfo(usbDevice) << "Drive" << deviceName << "does not contain dir subdirectory.";
        return;
    }
    auto destinationDirectory = currentDirectory;
    if (!destinationDirectory.mkpath("fileCache")) {
        qCCritical(usbDevice) << "Can't create fileCache subdirectory in"
                                     << destinationDirectory.absolutePath()
                                     << "directory";
        return;
    }
    if (!destinationDirectory.cd("fileCache")) {
        qCCritical(usbDevice) << "Can't change directory to fileCache subdirectory in"
                                     << destinationDirectory.absolutePath()
                                     << "directory";
        return;
    }
    struct PFilePair
    {
        PFile source, destination;
    };
    auto openSourceAndDestinationFiles = [&, destinationDirectory] (QFileInfo const & fileInfo) -> PFilePair
    {
        auto source = PFile::create(fileInfo.absoluteFilePath());
        QFile & sourceFile = *source;
        if (!sourceFile.open(QFile::ReadOnly)) {
            qCCritical(usbDevice) << "Can't open file" << sourceFile.fileName()
                                         << "to read:" << sourceFile.errorString();
            return {};
        }
        auto destination = PFile::create(destinationDirectory.absoluteFilePath(fileInfo.fileName()));
        QFile & destinationFile = *destination;;
        if (!destinationFile.open(QFile::ReadWrite | QFile::Truncate)) {
            qCCritical(usbDevice) << "Can't open file" << destinationFile.fileName()
                                         << "to write:" << destinationFile.errorString();
            return {};
        }
        return {qMove(source), qMove(destination)};
    };
    auto copyFiles = [&] (PFileList & files, PFilePair const & filePair)
    {
        if (filePair.source.isNull() || filePair.destination.isNull()) {
            return;
        }
        QFile & sourceFile = *filePair.source;
        QFile & destinationFile = *filePair.destination;
        qCInfo(usbDevice) << sourceFile.fileName() << "->" << destinationFile.fileName();
        constexpr int size = (1 << 20); // 1MiB
        QByteArray buffer{size, 0};
        char * const data = buffer.data();
        while (!sourceFile.atEnd()) {
            auto bytesRead = sourceFile.read(data, size);
            if (bytesRead < 0) {
                qCCritical(usbDevice) << "Can't read file" << sourceFile.fileName()
                                             << ":" << sourceFile.errorString();
                return;
            }
            auto bytesWritten = destinationFile.write(data, bytesRead);
            while (bytesWritten < bytesRead) {
                auto sizeWritten = destinationFile.write(data + bytesWritten, bytesRead - bytesWritten);
                if (sizeWritten < 0) {
                    qCCritical(usbDevice) << "Can't write file" << destinationFile.fileName()
                                                 << ":" << destinationFile.errorString();
                    return;
                }
                bytesWritten += sizeWritten;
            }
            Q_ASSERT(bytesWritten == bytesRead);
        }
        Q_ASSERT(sourceFile.size() == destinationFile.size());
        destinationFile.flush();
        files.append(filePair.destination);
    };
    QStringList nameFilters;
    nameFilters << "file.dat";
    // many other entries
    auto entryInfoList = sourceDirectory.entryInfoList(nameFilters, (QDir::Readable | QDir::Files));
    fileCopyFutureWatcher.setFuture(QtConcurrent::mappedReduced< PFileList >(qMove(entryInfoList), addResultType< PFilePair >(openSourceAndDestinationFiles), copyFiles));
}

// ...

在读取QFutureWatcher::finished 事件的结果后,所有目标文件仍然被我的应用程序打开和阻止。因此我可以得出结论,QSharedPointer&lt; QFile &gt; 的副本仍然存在。我怀疑他们是在QFuture 里面QFutureWatcher 离开的。如何在不使用假的QFuture 实例调用QFutureWatcher::setFuture 的情况下清除所有文件(即如何使QFile::~QFile() 关闭所有文件)?

为此,我需要从QFuture 窃取结果,而不是复制。

【问题讨论】:

    标签: qt c++14 shared-ptr future qtconcurrent


    【解决方案1】:

    我的解决方法如下所示:

    using PFile = QSharedPointer< QFile >;
    
    using PFileList = QList< PFile >;
    
    template< typename Result, typename Functor >
    struct FunctorWithResultType
            : std::decay_t< Functor >
    {
    
        using result_type = Result;
    
        FunctorWithResultType(Functor & functor)
            : std::decay_t< Functor >{std::forward< Functor >(functor)}
        { ; }
    
    };
    
    template< typename Result, typename Functor >
    FunctorWithResultType< Result, Functor >
    addResultType(Functor && functor)
    {
        return {functor};
    }
    
    class Test
    {
    public :
    
        QDir currentDirectory;
        // ...
    };
    
    // ...
    
    void Test::onDeviceAdded(QString deviceName)
    {
        qCInfo(usbDevice) << "USB device" << deviceName << "is added.";
        QDir sourceDirectory{deviceName};
        if (!sourceDirectory.cd("dir")) {
            qCInfo(usbDevice) << "Drive" << deviceName << "does not contain dir subdirectory.";
            return;
        }
        auto destinationDirectory = currentDirectory;
        if (!destinationDirectory.mkpath("fileCache")) {
            qCCritical(usbDevice) << "Can't create fileCache subdirectory in"
                                         << destinationDirectory.absolutePath()
                                         << "directory";
            return;
        }
        if (!destinationDirectory.cd("fileCache")) {
            qCCritical(usbDevice) << "Can't change directory to fileCache subdirectory in"
                                         << destinationDirectory.absolutePath()
                                         << "directory";
            return;
        }
        struct PFilePair
        {
            PFile source, destination;
        };
        auto openSourceAndDestinationFiles = [&, destinationDirectory] (QFileInfo const & fileInfo) -> PFilePair
        {
            auto source = PFile::create(fileInfo.absoluteFilePath());
            QFile & sourceFile = *source;
            if (!sourceFile.open(QFile::ReadOnly)) {
                qCCritical(usbDevice) << "Can't open file" << sourceFile.fileName()
                                             << "to read:" << sourceFile.errorString();
                return {};
            }
            auto destination = PFile::create(destinationDirectory.absoluteFilePath(fileInfo.fileName()));
            QFile & destinationFile = *destination;;
            if (!destinationFile.open(QFile::ReadWrite | QFile::Truncate)) {
                qCCritical(usbDevice) << "Can't open file" << destinationFile.fileName()
                                             << "to write:" << destinationFile.errorString();
                return {};
            }
            return {qMove(source), qMove(destination)};
        };
        auto copyFiles = [&] (PFileList & files, PFilePair const & filePair)
        {
            if (filePair.source.isNull() || filePair.destination.isNull()) {
                return;
            }
            QFile & sourceFile = *filePair.source;
            QFile & destinationFile = *filePair.destination;
            qCInfo(usbDevice) << sourceFile.fileName() << "->" << destinationFile.fileName();
            constexpr int size = (1 << 20); // 1MiB
            QByteArray buffer{size, 0};
            char * const data = buffer.data();
            while (!sourceFile.atEnd()) {
                auto bytesRead = sourceFile.read(data, size);
                if (bytesRead < 0) {
                    qCCritical(usbDevice) << "Can't read file" << sourceFile.fileName()
                                                 << ":" << sourceFile.errorString();
                    return;
                }
                auto bytesWritten = destinationFile.write(data, bytesRead);
                while (bytesWritten < bytesRead) {
                    auto sizeWritten = destinationFile.write(data + bytesWritten, bytesRead - bytesWritten);
                    if (sizeWritten < 0) {
                        qCCritical(usbDevice) << "Can't write file" << destinationFile.fileName()
                                                     << ":" << destinationFile.errorString();
                        return;
                    }
                    bytesWritten += sizeWritten;
                }
                Q_ASSERT(bytesWritten == bytesRead);
            }
            Q_ASSERT(sourceFile.size() == destinationFile.size());
            destinationFile.flush();
            files.append(filePair.destination);
        };
    
        auto & fileCopyFutureWatcher = *new QFutureWatcher< PFileList >{this};
    
        auto onFilesCopied = [&]
        {
            fileCopyFutureWatcher.deleteLater();
            PFileList copiedFiles = fileCopyFutureWatcher.result();
            qCInfo(usbDevice) << "Files copy operation from drive finished.";
            qCInfo(usbDevice) << copiedFiles.size();
            for (PFile const & file : copiedFiles) {
                // ...
            }
        };
        connect(&fileCopyFutureWatcher, &fileCopyFutureWatcher.finished, onFilesCopied);
    
        QStringList nameFilters;
        nameFilters << "*.dat";
        auto entryInfoList = sourceDirectory.entryInfoList(nameFilters, (QDir::Readable | QDir::Files));
        fileCopyFutureWatcher.setFuture(QtConcurrent::mappedReduced< PFileList >(qMove(entryInfoList), addResultType< PFilePair >(openSourceAndDestinationFiles), copyFiles));
    }
    
    // ...
    

    我几乎可以肯定,fileCopyFutureWatcher 实例将可靠地成为deleted。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-17
      • 2016-07-08
      • 1970-01-01
      • 2019-08-05
      • 1970-01-01
      • 2018-04-06
      相关资源
      最近更新 更多