3

我正在我的类中实现一个方法,它将数据从 TableView 对象写入 CSV 文件。但是,当程序运行时,程序会以非常慢的速度(3 或 4 秒)将数据写入 USB 驱动器上的文件,但可以在系统的内部驱动器上正常工作。这是因为我在写入文件后没有使用flush()或close()吗?

这是我的代码

bool ThicknessCalibrationDataDisplay::WriteCSVFileChanges()
{
    QModelIndex tableViewModelindex =  tableViewModel_->index(0,0);

    QFile file(CSVFileName_);
    if(!file.exists())
        return false;

    if(!file.open(QIODevice::WriteOnly))
        return false;


    for(int i = 0; i < totalRows_ ; i++)
    {
        for(int j = 0 ; j < totalColumns_; j++)
        {
            tableViewModelindex =  tableViewModel_->index(i,j);
            qDebug()<<tableViewModelindex.data();
            QString text = tableViewModelindex.data().toString();
            QTextStream OutputStream(&file);

            if(j == totalColumns_ - 1)
                OutputStream<<"\n\r";
            else
                OutputStream<<',';

        }
    }

}

这是我之前的代码,现在我打算关闭文件流,以便优雅地退出。QFile::close() 的 Qt API 说

调用 QFile::flush() 并关闭文件。来自刷新的错误被忽略。

所以我应该只调用close(),还是调用flush(),记录任何错误然后调用close()更好?

是否有任何其他修改,我必须做,以改进写操作?

4

2 回答 2

8
  1. flush()vs.是一条红鲱鱼,它根本close()不会影响你的表现。

  2. 销毁 a 会QTextStream强制刷新文件。刷新文件很。每次迭代循环时,您都会破坏文本流!在循环之外创建流!

    这是来自 Qt 5.1.1 的源代码:

    QTextStream::~QTextStream()
    {
        if (!d->writeBuffer.isEmpty())
            d->flushWriteBuffer();
    }
    

    QSaveFile在 Qt 5.1 或更高版本上,如果要确保在文件关闭后刷新磁盘缓冲区,则应该使用。只有这样才能确保一旦您认为已完成保存,数据实际上就在磁盘上。

  3. QTextStream有自己的缓冲区。因此,如果您想在刷新时捕获错误,则需要flush()在流本身上使用该方法,而不是在QFile.

  4. 从 GUI 线程进行任何文件访问是一个非常糟糕的主意。任何时候任何事情都会阻塞,你的 UI 线程也会阻塞。

    现在的问题是:如何使您的模型可以安全地从另一个线程访问?如果您使用自定义模型,您可能只需要在写入文件期间将模型切换为只读模式。只读模式将是模型的自定义属性,因此所有setData调用都会失败。您当然需要向用户表明这一点。模型的视图将是只读的,但这比阻止整个 GUI 友好得多。

    如果您认为仅使用 QMutex 阻止对模型的并发访问就足够了,请再想一想。对模型所做的任何修改都可能会改变其结构,因此您的编写者需要正确处理模型发出的所有信号。这会使作者变得更加复杂。临时只读模型可让您拥有响应式 GUI,但会给用户带来暂时的不便,同时代码复杂性的增加最小。

    class MyModel : public QStandardItemModel /*  or whatever other class you derive from */ {
        typedef QStandardItemModel inherited;
        Q_OBJECT
        Q_PROPERTY(bool writable READ isWritable WRITE setWritable NOTIFY writableChanged)
        bool m_writable;
    public:
        explicit MyModel(QObject * parent) : inherited(parent), m_writable(true) {}
        Q_SLOT void setReadOnly() {
            if (!m_writable) return;
            m_writable = false; 
            emit writableChanged(m_writable);
        }
        Q_SLOT void setReadWrite(){
            if (m_writable) return;
            m_writable = true; 
            emit writableChanged(m_writable);
        }
        Q_SIGNAL void writableChanged(bool);
        bool isWritable() const { return m_writable; }
        void setWritable(bool writable) {
           if (m_writable == writable) return;
            m_writable = writable; 
            emit writableChanged(m_writable);
        }
        bool setData(const QModelIndex & index, const QVariant & val, int role = Qt::EditRole) {
            if (! m_writable) return false;
            return inherited::setData(index, val, role);
        }
    };
    
  5. 一些 USB 驱动器非常慢。

您重新编写的代码至少应如下所示:

bool ThicknessCalibrationDataDisplay::WriteCSVFileChanges()
{
#if QT_VERSION < QT_VERSION_CHECK(5,1,0)
    QFile file(CSVFileName_);
#else
    QSaveFile file(CSVFileName_); // does a disk commit at the end
#endif
    if(!file.exists())
        return false;

    if(!file.open(QIODevice::WriteOnly))
        return false;

    QTextStream os(&file);
    for(int i = 0; i < totalRows_ ; i++)
    {
        for(int j = 0 ; j < totalColumns_; j++)
        {
            QModelIndex index = tableViewModel_->index(i,j);
            qDebug() << index.data();
            os << index.data();
            if(j == totalColumns_ - 1)
                os<<"\n\r";
            else
                os<<',';

        }
    }
    os.flush();
#if QT_VERSION >= QT_VERSION_CHECK(5,1,0)
    return os.status() == QTextStream::Ok && file.commit();
#else
    return os.status() == QTextStream::Ok;
#endif
}
于 2013-09-30T14:34:28.770 回答
7

我可以告诉你,它QFile::close()是在函数末尾隐式调用的,因为 QFile 对象的析构函数是在文件变量范围的末尾调用的。从 QFile 文档:

QFile::~QFile ()
Destroys the file object, closing it if necessary.

您可能想要尝试的是操作系统的底层同步功能。AFAIK Qt 没有为此提供任何 API,因此它不能移植。调用该QFile::flush()函数时,可能发生的情况(至少在 Linux 上)是数据从用户空间缓冲区刷新到底层系统缓存,而不一定是磁盘。在 linux/unix 上,您需要fsync确保文件实际写入磁盘的功能。代码看起来像:

file.flush();
fsync( file.handle() );
file.close();

我相信相同的代码也可以在 Windows 上运行。

有关 fflush 与 fsync 的更多信息,请参阅fflush 和 fsync 之间的区别

于 2013-09-30T14:54:50.690 回答