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();
    }
    

    Qt 5.1 以降ではQSaveFile、ファイルが閉じられたときにディスク バッファーが確実にフラッシュされるようにする場合に使用する必要があります。これだけで、保存が完了したと思ったら、データが実際にディスク上にあるという保証が得られます。

  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.

おそらく試してみたいのは、オペレーティング システムの根底にある同期機能です。私の知る限り、Qt はこのための API を提供していないため、移植できません。関数が呼び出されると、QFile::flush()(少なくとも Linux では) データがユーザー空間バッファーから基になるシステム キャッシュにフラッシュされ、必ずしもディスクにフラッシュされるとは限りません。fsyncLinux/Unix では、ファイルが実際にディスクに書き込まれることを確認する関数が必要です。コードは次のようになります。

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

同じコードが Windows でも動作すると思います。

fflush と fsync の詳細については、fflush と fsync の違いを参照してください。

于 2013-09-30T14:54:50.690 に答える