2016-03-28 17 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; 

Bir çalışan iş parçacığında birkaç resim yüklemeyi ve bunları ana iş parçacığıyla dışarı çekmeyi deniyorum. Ama emin değilim ki, resimdeki iş parçacığını çizdikten sonra silmek mümkün değil.Qt, boya resmi, bu hafıza sorununa neden olur mu?

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

Yukarıdaki kod sorunlara neden olacak mı? DrawImage işlevi bir "const reference tarafından geçirilir" olduğundan. Bu herhangi bir hafıza sorununa neden olur mu?

delete tImg başka bir iş parçacığında ne varsa? delete tImg'un yalnızca painter->drawImage(x, y, *tImg);

+1

Neden "QImage" öğesini yığında ayırmıyorsunuz? – Drop

+0

Ben sadece, fonksiyonun gelecekte olası hataları önlemek için nasıl çalıştığını merak ediyorum. – Nyaruko

+1

Bu davranış belgelenmiştir. Genelde const referansının her zaman geçerli olduğunu varsayarız (eğer olmadığını doğrulamanın bir yolu yoktur ve tasarım gereğidir). Bir referans için temel depolama alanını dağıtmak asla iyi bir fikir değildir. Ve kesinlikle, 'QPainter' iş parçacığı güvenli değildir. Şüphe durumunda, açık kaynaklı kütüphanenin iç yapısını anlamanın en iyi yolu, kaynak kodunu indirmek ve okumaktır. Muhtemelen en iyi bahistir. – Drop

cevap

3

'dan sonra çağrıldığından emin olmak için mutex kullanırsam güvenli olur mu? Manuel bellek yönetimi gereksizdir. Bunu yapmak için Qt'den faydalanmalısınız. Görüntüyü bir sinyal yuvası bağlantısı üzerinden geçirebilir ve değerin otomatik olarak kopyalanacağını ve herhangi bir erişimin otomatik olarak Qt ile senkronize edileceğini kullanabilirsiniz.

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

Öncelikle görüntü kaynağıdır bir sinyal ile bir sınıf atalım: Burada

Eğer derleyici icar, çok basit, bunu sizin için kaynak yönetiminin tüm zor işleri yapabileceğini nasıl. İş parçacığı sınırlarının çaprazlanması gerektiğinde, herhangi bir kopyalama işlemi Qt tarafından otomatik olarak yapılacağından, görüntüyü bir const referans türünde sağlayan bir sinyale sahiptir.

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

Bir yöntem görüntüyü oluşturur ve sinyali yayar. Otomatik bağlantılar hasImage sinyali ile kullanıldığı sürece, bu yöntem herhangi bir iş parçacığında güvenle kullanılabilir. Bizim durumumuzda, bu yöntemi her zaman çalışan iş parçacığından çalıştırırız, fakat onu ana iş parçasından da çalıştırabiliriz - tek fark performansta olurdu.

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

Biz bu sınıfın bir örneğini gerekir ve bir şey görüntüyü göstermek için - bir QLabel, diyelim:

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

Şimdi etiket en ayarlayan bir functor hasImage bağlayabilirsiniz görüntüyü boyutlandırır ve ayarlar. Daha sonra görüntü jeneratörünü hemen global havuzdan bir çalışan iş parçacığında çalıştırır. Bu otomatik olarak QtConcurrent::run tarafından işlenir.

Functor ana iş parçacığı içinde çalışır: bu, içerik parametresini connect: connect(--, --, context, --)'a sağlayarak sağlanır. Functor, istediğimiz gibi label.thread()'da çalışır.

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

bağlantısı otomatik olduğundan, alıcı nesneye yuvası çağrısı (label) parçacığının olay kuyruk yayınlanmasından içinde hasImage sinyal sonuçlarını çağırarak etkisi - ana iş parçacığı burada, kuyruk. Olay döngüsü, yuva çağrısını alır ve yürütür. Böylece, hasImage aracılığıyla çalışan bir iş parçacığı içinde bile çağrıldı, görüntü otomatik olarak kopyalanır ve ana iş parçacığındaki çeviricimize iletilir.

Son olarak, işlemi başlatmak için ilk resmi oluştururuz.sonunda #include

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

sinyal hasImage sinyalin uygulanmasını ve ImageSource sınıfını anlatan meta sağlamak için gereklidir. Moc tarafından üretilmiştir.

#include "main.moc" 

Tam kod bu, yeni bir projeye yapıştırabilir, derleyebilir ve çalıştırabilirsiniz; veya komple projeyi github bağlantısından indirebilirsiniz.

Pixmap'ını makinemde yaklaşık 1000/s hızında güncellenen bir etiketi gösterir. Uygulama tamamen duyarlı: Pencereyi serbestçe hareket ettirebilir ve istediğiniz zaman çıkabilirsiniz.

Dişli görüntü yükleyicinin başka bir örneği için bkz. this answer.

İlgili konular