2016-03-28 9 views
1
//Case 1: 
QImage* tImg = new QImage("Some Image Here"); 
painter->drawImage(x, y, *tImg); 
... 
delete tImg; 

//Case 2: 
QImage* tImg = new QImage("Some Image Here"); 
{ 
    QImage aImg(*tImg); 
    painter->drawImage(x, y, aImg); 
} 
... 
delete tImg; 

私は、ワーカースレッドで複数の画像を読み込み、それらをメインスレッドに描画しようとしています。しかし、私はそれを描画した後、ワーカースレッドでイメージを削除することは大丈夫ですか?Qt、ペイントイメージ、メモリに問題がありますか?

//Case 3: 
... 
//In worker thread 
QImage* tImg = new QImage("Some Image Here"); 
mutex.lock(); 
matrix.insert(tImg); // matrix is a QList 
mutex.unlock(); 
... 
//In main thread 
mutex.lock(); 
foreach(QImage* tImg, matrix) 
{ 
    painter->drawImage(x, y, *tImg); 
} 
mutex.unlock(); 
... 
//In worker thread 
mutex.lock(); 
matrix.remove(tImg); 
delete tImg; 
mutex.unlock(); 

上記のコードで問題が発生しますか? drawImage関数は "const参照によるパス"です。これはメモリの問題を引き起こしますか?

delete tImgが別のスレッドにある場合はどうなりますか? delete tImgが後で呼び出されるようにmutexを使用すると安全でしょうかpainter->drawImage(x, y, *tImg);

+1

スタックに 'QImage'を割り当ててみませんか? – Drop

+0

私は、将来的に起こりうるバグを回避するためにどのように機能するのだろうか。 – Nyaruko

+1

この現象は説明されていません。我々は通常const参照が常に有効であると仮定します(存在しないかどうかを検証する方法はありません。基礎となるストレージの参照を解放することは、決して良い考えではありません。もちろん、 'QPainter'はスレッドセーフではありません。不明な点がある場合は、オープンソースライブラリの内部を理解する最善の方法は、ソースコードをダウンロードして読むことです。それはおそらくあなたの最善の策です。 – Drop

答えて

3

マニュアルメモリ管理は不要です。あなたはQtを活用してそれを行うべきです。シグナルスロット接続を介して画像を渡すことができ、その値が自動的にコピーされるという事実を利用することができ、そのアクセスはQtによって自動的に同期されます。

は、ここでコンパイラをさせる、非常に簡単に、あなたがそれを行うことができる方法ですあなたのためのリソース管理のすべてのハードワークを行います。

// https://github.com/KubaO/stackoverflown/tree/master/questions/imageloader-36265788 
#include <QtWidgets> 
#include <QtConcurrent> 

まずは、映像ソースである信号を持つクラスを持ってみましょう。スレッドの境界を越える必要がある場合は、Qtによって自動的にコピーが行われるため、const参照型のイメージを提供するシグナルがあります。

class ImageSource : public QObject { 
    Q_OBJECT 
public: 
    Q_SIGNAL void hasImage(const QImage & image); 

メソッドがイメージを生成し、シグナルを放出します。 hasImageシグナルで自動接続が使用されている限り、このメソッドは任意のスレッドで安全に実行できます。私たちの場合、私たちは常にワーカースレッドからこのメソッドを実行しますが、メインスレッドからもこのメソッドを実行できます。唯一の違いはパフォーマンスです。

/// This method is thread-safe (ignoring the UB of incrementing a shared int) 
    void generate() { 
     static auto counter = 0; 
     QImage img(128, 128, QImage::Format_ARGB32); 
     img.fill(Qt::white); 
     QPainter p(&img); 
     p.drawText(img.rect(), Qt::AlignCenter, QString::number(counter++)); 
     p.end(); 
     emit hasImage(img); 
    } 
}; 

私たちは、そのクラスのインスタンスが必要になりますし、何かが上の画像を表示する - QLabel、言う:

int main(int argc, char ** argv) { 
    QApplication app{argc, argv}; 
    ImageSource source; 
    QLabel label; 
    label.show(); 

我々は今、ラベルの設定ファンクタにhasImageを接続することができますサイズを設定し、画像を設定します。その後、グローバルプールから直ちにワーカースレッドでイメージジェネレータを再度実行します。それはQtConcurrent::runによって自動的に処理されます。

ファンクタはメインスレッドで実行されます。これは、コンテキストパラメータをconnectconnect(--, --, context, --)に指定することで保証されます。ファンクタはlabel.thread()で動作します。

QObject::connect(&source, &ImageSource::hasImage, &label, [&](const QImage & image){ 
     label.setFixedSize(image.size()); 
     label.setPixmap(QPixmap::fromImage(image)); 
     QtConcurrent::run(&source, &ImageSource::generate); 
    }); 

接続は、受信対象のスロットコール(label)スレッドのイベントキューの転記にhasImage信号の結果を呼び出す効果自動であるので - ここで、メインスレッドのキュー。イベントループは、スロットコールをピックアップして実行します。したがって、ワーカースレッドでhasImageを呼び出しても、イメージは自動的にコピーされ、メインスレッドのファンクタに渡されます。

最後に、最初のイメージを生成してプロセスを開始します。終わりに

QtConcurrent::run(&source, &ImageSource::generate); // generate the first image 
    return app.exec(); 
} 

#includeは、信号hasImage信号の実装、ImageSourceクラスを記述するメタデータを提供するために必要とされます。これはmocによって生成されます。

#include "main.moc" 

これは完全なコードです。これを新しいプロジェクトに貼り付けてコンパイルして実行することができます。またはgithubのリンクから完全なプロジェクトをダウンロードしてください。

私のマシンで約1000/sの速度でピックスマップを更新するラベルを表示します。アプリケーションは完全に応答します。ウィンドウを自由に移動し、いつでも終了できます。

スレッドイメージローダーの別の例については、this answerを参照してください。