2016-08-30 6 views
1

QFileから読み込みたい別のスレッドにある最適な読み込み方法は何でしょうか?異なるスレッドのQFileからの読み込み

は考えてみましょう:

class AFile : public QObject 
{ 
    Q_OBJECT 
public: 
    AFile(const QString &name, QObject *parent = Q_NULLPTR) : QObject(parent), m_File(name) { m_File.open(QIODevice::ReadWrite); } 

public slots: 
    void write(const QByteArray &data, qint64 pos) { m_File.seek(pos); m_File.write(data); } 

private: 
    mutable QFile m_File; 
}; 

class AData : public QObject 
{ 
    Q_OBJECT 
public: 
    using QObject::QObject; 

    void save(const QByteArray &data) { emit saveData(data, 0); } 

signals: 
    void saveData(const QByteArray &data, qint64 pos) const; 
}; 

AFile file("myfile"); 
AData data; 
QThread *thread = new QThread; 

connect(&data, &AData::saveData, &file, &AFile::write); 

file.moveToThread(&thread); 
thread.start(); 
data.save("Some data"); 

//how to concurrently read though? 

AFileは、ファイルへの書き込みをすべて処理しますQFileのラッパーです。高価なディスク書き込み操作でメインスレッドを遅くしないために、別のスレッドに移動されます。複数の読み込み状況はロックやミューテックスによって処理されますが、メインスレッド(読み込みたい)は書き込みが完了するまで待たなければならないため、最初に別のスレッドにファイルを格納する目的を無効にしますそのような場合、私はそれをメインスレッドに残すことができます。

私は非常に好きではないオプションは、シグナルとスロットです。なぜなら、私はちょっと重い体重に見えるからです。私は基本的にデータの要求を送信し、それが読み取られるのを待ちます(データでデータを返すか、変数を送信してデータで満たされ、完了した信号を待つ)。

これは最良のアプローチだと思われますが、私はそれが良いデザインであるとは本当に確信していません。

+0

は本当にあなたのアプリを遅くファイルに書き込みますか?私はあなたがいくつかの同期モードを強制しない限り、OSがそれらをキャッシュすることを期待します。また、同期せずに独立してファイルを読み書きすることを正しく理解していますか?私はあなたがそれの別の部分で読んで書いていると仮定します。それで、あなたは単にファイルを2回開くことができます。 – michalsrb

+1

@michalsrb GUIスレッドから実行されるすべての同期I/Oは、ユーザビリティの問題の主な原因であり、GUIはハングします。イベントループ駆動UI APIの1日目以降はそうでした。これがUniversal Windows Platformなどの最新のAPIが主に非同期APIを持つ理由です.I/Oを待っているUIがブロックされていると、ユーザーに悪い経験をもたらします。 –

答えて

3

同時読み込みは、要求と表示部分を分割して行います。最初にデータを要求し、読み取り側が読み取った後、データが読み取られたという指示に反応します。

ファイルオブジェクトはワーカースレッドからアクセスされるので、私はADataクラスの中にカプセル化しました。クラスをワーカースレッドに移動することができます。外部から呼び出した信号はスレッドセーフです。

QFileの周りに非同期ラッパーを作成することも可能ですが、正しく実行するにはQtの実装の詳細(core_privateモジュール)を使用する必要があります。

// https://github.com/KubaO/stackoverflown/tree/master/questions/async-file-io-39226814 
#include <QtWidgets> 

class AData : public QObject 
{ 
    Q_OBJECT 
    QFile m_file; 
public: 
    explicit AData(QObject * parent = nullptr) : QObject{parent} { 
     connect(this, &AData::save, this, [=](const QByteArray & data, qint64 pos){ 
      m_file.seek(pos); 
      m_file.write(data); 
     }); 
     connect(this, &AData::load, this, [=](qint64 pos, qint64 len){ 
      m_file.seek(pos); 
      if (len == -1) len = m_file.size(); 
      auto data = m_file.read(len); 
      emit loaded(data, pos); 
     }); 
    } 
    bool open(const QString & name) { 
     m_file.setFileName(name); 
     return m_file.open(QIODevice::ReadWrite); 
    } 
    Q_SIGNAL void save(const QByteArray &data, qint64 pos = 0) const; 
    Q_SIGNAL void load(qint64 pos, qint64 len) const; 
    Q_SIGNAL void loaded(const QByteArray &data, qint64 pos) const; 
}; 

テストUIは、それを使用する方法を示します。

int main(int argc, char ** argv) { 
    QApplication app{argc, argv}; 
    struct Thread : QThread { 
     ~Thread() { quit(); wait(); } 
    } ioThread; 
    AData data; 
    data.open("myfile"); 
    data.moveToThread(&ioThread); 
    ioThread.start(); 

    QWidget ui; 
    QGridLayout layout{&ui}; 
    QTextEdit text; 
    QPushButton load{"Load"}; 
    QPushButton save{"Save"}; 
    QPushButton clear{"Clear"}; 
    layout.addWidget(&text, 0, 0, 1, 2); 
    layout.addWidget(&load, 1, 0); 
    layout.addWidget(&save, 1, 1); 
    layout.addWidget(&clear, 2, 0, 1, 2); 
    ui.show(); 

    using Q = QObject; 
    Q::connect(&load, &QPushButton::clicked, &data, [&]{ 
     data.load(0, -1); 
    }); 
    Q::connect(&data, &AData::loaded, &app, [&](const QByteArray & data, qint64){ 
     text.setPlainText(QString::fromUtf8(data)); 
    }); 
    Q::connect(&save, &QPushButton::clicked, &data, [&]{ 
     data.save(text.document()->toPlainText().toUtf8()); 
    }); 
    Q::connect(&clear, &QPushButton::clicked, &text, &QTextEdit::clear); 

    return app.exec(); 
} 
#include "main.moc" 
+0

これはかなりいいと簡潔です! QtConcurrentを使用すると競合状態が発生しないのですか?書き込みが実行されていて、QtConcurrentによってスレッドにディスパッチされているとします。終了する前に、同じ場所にもディスパッチされた読書があります。何が起こるか? QtConcurrentは何らかの形でこの状況を守っていますか? m_Fileは私が理解しているように、ここで共有されているリソースです。 – Resurrection

+0

これは共有リソースであり、 'QFile'がそのようなアクセスに対して回復力があるかどうかはわかりません。一度に1つのアクセスだけが実行されていることを保証することは可能です。後でそれを編集します。 –

+0

私はすでに試してみましたが、弾力性はありません。それを解決するために、私は最初にロックを試みましたが、最終的にQtConcurrentを使用せず、ラッパーを単一の他のスレッドに移動しました。メイン(GUI)スレッドが遅くならず、すべてのアクセスが決して保証されないためすべてが単一のスレッドで実行されるので、並列です。私はあなたの解決策を楽しみにしています(おそらく私よりも優れています:-))。 – Resurrection

関連する問題