İzlenecek yol: Kullanıcı arabirimi akıştan çalışma kaldırma
Bu belge, Microsoft Foundation Classes (mfc) uygulamasında bir iş parçacığı için kullanıcı arabirimi (UI) iş parçacığı tarafından gerçekleştirilen iş taşımak eşzamanlılık çalışma zamanı kullanımı gösterilmiştir. Bu belge de uzun bir çizim işlemi performansını gösterilmiştir.
İş durdurma işlemlerini devrederek UI akıştan kaldırmak, örneğin, iş parçacıkları için çizim uygulamanızın yanıt verme hızını artırabilir. Bu izlenecek yolda, uzun engelleyici bir işlem göstermek için Mandelbrot düzgünleştiren parçalı üreten bir çizim yordamı kullanır. Her pikselin hesaplaması tüm hesaplamaları bağımsız Mandelbrot düzgünleştiren parçalı nesil de parallelization için iyi bir aday olmasıdır.
Önkoşullar
Bu izlenecek yolda, başlamadan önce aşağıdaki konuları okuyun:
mfc application development temellerini anlamak da öneririz ve GDI+ bu yönergeyi başlamadan önce. MFC hakkında daha fazla bilgi için, bkz. MFC Masaüstü Uygulamaları. Hakkında daha fazla bilgi için GDI+, bakın GDI +.
Bölümler
Bu izlenecek yolda, aşağıdaki bölümleri içerir:
mfc uygulaması oluşturma
Seri Mandelbrot uygulamanın sürümünü uygulayan
Kullanıcı arabirimi akıştan çalışma kaldırma
Çizim performansı artırma
İptal etmek için destek ekleme
mfc uygulaması oluşturma
Bu bölümde, temel mfc uygulaması oluşturma açıklanmaktadır.
Visual C++ mfc uygulaması
Dosya menüsünde, Yeni'yi ve sonra da Proje'yi tıklatın.
İçinde Yeni bir proje iletişim kutusu Yüklü şablonlar bölmesinde seçin **Visual C++**ve daha sonra şablonları bölmesinde seçin mfc uygulaması. Örneğin, proje için bir ad yazın Mandelbrotve i Tamam görüntülemek için mfc uygulaması Sihirbazı.
İçinde Uygulama türü bölmesinde seçin tek belge. Emin Belge/görünüm mimarisi desteği onay kutusu temizlenir.
Tıklatın Son proje oluşturmak ve kapatmak için mfc uygulaması Sihirbazı.
Oluşturma ve çalışan uygulama başarıyla oluşturulduğunu doğrulayın. Uygulama üzerinde oluşturmak için Yapı menüsünde'ı Build Solution. Uygulama başarıyla oluşturur, tıklatarak uygulamayı çalıştırmak Start Debugging komutunu , Debug menü.
Seri Mandelbrot uygulamanın sürümünü uygulayan
Bu bölümde, Mandelbrot düzgünleştiren parçalı çizmek açıklamaktadır. Bu sürüm için Mandelbrot düzgünleştiren parçalı çizer bir GDI+Bitmap nesnesini ve sonra istemci penceresi bitmapi içeriğini kopyalar.
Seri Mandelbrot uygulamanın sürümünü uygulamak için
Stdafx.h aşağıdakileri ekleyin #include yönergesi:
#include <memory>
ChildView.h, sonra pragma yönergesi, tanımladığınız BitmapPtr türü. BitmapPtr Türü işaretçisi sağlayan bir Bitmap nesnesine birden çok bileşenleri tarafından paylaşılır. Bitmap , Artık herhangi bir bileşen tarafından başvurulan nesne silinir.
typedef std::shared_ptr<Gdiplus::Bitmap> BitmapPtr;
ChildView.h aşağıdaki kodu ekleyin protected bölümünde CChildView sınıfı:
protected: // Draws the Mandelbrot fractal to the specified Bitmap object. void DrawMandelbrot(BitmapPtr); protected: ULONG_PTR m_gdiplusToken;
ChildView.cpp, yorum veya aşağıdaki satırları kaldırın.
//#ifdef _DEBUG //#define new DEBUG_NEW //#endif
Hata ayıklama yapılarında bu adımı kullanarak uygulamanın engelleyen DEBUG_NEW ile uyumsuz ayırıcı GDI+.
ChildView.cpp eklemek bir using emrini Gdiplus ad.
using namespace Gdiplus;
Yapıcı ve yıkıcı, aşağıdaki kodu ekleyip CChildView başlatma ve kapatma için sınıf GDI+.
CChildView::CChildView() { // Initialize GDI+. GdiplusStartupInput gdiplusStartupInput; GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL); } CChildView::~CChildView() { // Shutdown GDI+. GdiplusShutdown(m_gdiplusToken); }
Implement CChildView::DrawMandelbrot yöntemi. Bu yöntem belirtilen Mandelbrot düzgünleştiren parçalı çizer Bitmap nesnesi.
// Draws the Mandelbrot fractal to the specified Bitmap object. void CChildView::DrawMandelbrot(BitmapPtr pBitmap) { if (pBitmap == NULL) return; // Get the size of the bitmap. const UINT width = pBitmap->GetWidth(); const UINT height = pBitmap->GetHeight(); // Return if either width or height is zero. if (width == 0 || height == 0) return; // Lock the bitmap into system memory. BitmapData bitmapData; Rect rectBmp(0, 0, width, height); pBitmap->LockBits(&rectBmp, ImageLockModeWrite, PixelFormat32bppRGB, &bitmapData); // Obtain a pointer to the bitmap bits. int* bits = reinterpret_cast<int*>(bitmapData.Scan0); // Real and imaginary bounds of the complex plane. double re_min = -2.1; double re_max = 1.0; double im_min = -1.3; double im_max = 1.3; // Factors for mapping from image coordinates to coordinates on the complex plane. double re_factor = (re_max - re_min) / (width - 1); double im_factor = (im_max - im_min) / (height - 1); // The maximum number of iterations to perform on each point. const UINT max_iterations = 1000; // Compute whether each point lies in the Mandelbrot set. for (UINT row = 0u; row < height; ++row) { // Obtain a pointer to the bitmap bits for the current row. int *destPixel = bits + (row * width); // Convert from image coordinate to coordinate on the complex plane. double y0 = im_max - (row * im_factor); for (UINT col = 0u; col < width; ++col) { // Convert from image coordinate to coordinate on the complex plane. double x0 = re_min + col * re_factor; double x = x0; double y = y0; UINT iter = 0; double x_sq, y_sq; while (iter < max_iterations && ((x_sq = x*x) + (y_sq = y*y) < 4)) { double temp = x_sq - y_sq + x0; y = 2 * x * y + y0; x = temp; ++iter; } // If the point is in the set (or approximately close to it), color // the pixel black. if(iter == max_iterations) { *destPixel = 0; } // Otherwise, select a color that is based on the current iteration. else { BYTE red = static_cast<BYTE>((iter % 64) * 4); *destPixel = red<<16; } // Move to the next point. ++destPixel; } } // Unlock the bitmap from system memory. pBitmap->UnlockBits(&bitmapData); }
Implement CChildView::OnPaint yöntemi. Bu yöntem çağrıları CChildView::DrawMandelbrot ve daha sonra içeriğini kopyalar Bitmap pencere nesnesi.
void CChildView::OnPaint() { CPaintDC dc(this); // device context for painting // Get the size of the client area of the window. RECT rc; GetClientRect(&rc); // Create a Bitmap object that has the width and height of // the client area. BitmapPtr pBitmap(new Bitmap(rc.right, rc.bottom)); if (pBitmap != NULL) { // Draw the Mandelbrot fractal to the bitmap. DrawMandelbrot(pBitmap); // Draw the bitmap to the client area. Graphics g(dc); g.DrawImage(pBitmap.get(), 0, 0); } }
Oluşturma ve çalışan uygulama başarıyla güncelleştirildi doğrulayın.
Mandelbrot uygulama sonuçları aşağıda gösterilmiştir.
Her piksel için hesaplaması çok fazla olduğundan, UI iş parçacığı genel SD bitene kadar ek iletiler işleyemiyor. Bu uygulamanın yanıt verme becerisi azaltabilirsiniz. Ancak UI akıştan çalışma kaldırarak bu sorunu hafifletmek.
Top
UI akıştan çalışma kaldırma
Bu bölümde, UI Mandelbrot uygulama iş parçacığında çizim çalışma kaldırma gösterilmiştir. Bir çalışan iş parçacığı UI akıştan çizim çalışma taşıyarak, iş parçacığı arka planda yansıma oluşturur gibi UI iş parçacığı iletilerini işleyebilir.
Eşzamanlılık çalışma zamanı görevleri çalıştırmak için üç yol sağlar: Görev grupları, zaman uyumsuz aracıları, ve basit görevleri. İş UI akıştan kaldırmak için bu mekanizmaların herhangi birini kullanabilirsiniz, ancak bu örnek kullanır bir concurrency::task_group iptali görev gruplarını desteklemek için nesne. Bu izlenecek yolda, iptali daha sonra istemci penceresi yeniden boyutlandırıldığında gerçekleştirilen çalışma miktarını azaltmak ve pencere bozulduğunda temizleme gerçekleştirmek için kullanır.
Bu örnek ayrıca kullanan bir concurrency::unbounded_buffer UI iş parçacığı ve birbirleriyle iletişim kurabilmeleri için çalışan iş parçacığı nesnesi. İş parçacığı görüntüsü üretir sonra işaretçi gönderir Bitmap itiraz unbounded_buffer nesne ve Boya ileti UI iş parçacığına deftere nakleder. UI iş parçacığı sonra aldığında unbounded_buffer nesnesinin Bitmap nesne ve istemci penceresi çizer.
Çizim çalışmaları UI akıştan kaldırmak için
Stdafx.h aşağıdakileri ekleyin #include yönergeleri:
#include <agents.h> #include <ppl.h>
ChildView.h içinde ekleme task_group ve unbounded_buffer üye değişkenleri protected bölümünde CChildView sınıfı. task_group Nesnesi tutar; çizim gerçekleştirdiğiniz görevleri unbounded_buffer tamamlanan Mandelbrot Görüntü nesnesi tutar.
concurrency::task_group m_DrawingTasks; concurrency::unbounded_buffer<BitmapPtr> m_MandelbrotImages;
ChildView.cpp eklemek bir using emrini concurrency ad.
using namespace concurrency;
De CChildView::DrawMandelbrot yöntemi çağrısının Bitmap::UnlockBits, çağrı concurrency::send geçirmek için işlev Bitmap UI iş parçacığı nesnesi. Sonra UI iş parçacığı bir Boya ileti postalamak ve istemci alanı geçersiz kılar.
// Unlock the bitmap from system memory. pBitmap->UnlockBits(&bitmapData); // Add the Bitmap object to image queue. send(m_MandelbrotImages, pBitmap); // Post a paint message to the UI thread. PostMessage(WM_PAINT); // Invalidate the client area. InvalidateRect(NULL, FALSE);
Güncelleştirme CChildView::OnPaint yöntemi güncelleştirilmiş almak için Bitmap nesne ve istemci pencerenin resim çizmek.
void CChildView::OnPaint() { CPaintDC dc(this); // device context for painting // If the unbounded_buffer object contains a Bitmap object, // draw the image to the client area. BitmapPtr pBitmap; if (try_receive(m_MandelbrotImages, pBitmap)) { if (pBitmap != NULL) { // Draw the bitmap to the client area. Graphics g(dc); g.DrawImage(pBitmap.get(), 0, 0); } } // Draw the image on a worker thread if the image is not available. else { RECT rc; GetClientRect(&rc); m_DrawingTasks.run([rc,this]() { DrawMandelbrot(BitmapPtr(new Bitmap(rc.right, rc.bottom))); }); } }
CChildView::OnPaint Yöntemi, bir iletinin arabellekte yoksa Mandelbrot görüntü oluşturmak için bir görev oluşturur. İleti arabelleği yok içerecek bir Bitmap nesne durumlarda ilk Boya iletisi gibi ve başka bir pencere önünde İstemcisi penceresinde taşınır.
Oluşturma ve çalışan uygulama başarıyla güncelleştirildi doğrulayın.
Arka planda çizim çalışma gerçekleştirildiğinden UI şimdi daha iyi tepki.
Top
Çizim performansı artırma
Her pikselin hesaplaması tüm hesaplamaları bağımsız olduğundan Mandelbrot düzgünleştiren parçalı nesil parallelization için iyi bir adaydır. Çizim yordamı parallelize, dış dönüştürmek for içinde döngü CChildView::DrawMandelbrot yöntemine yapılan bir çağrı concurrency::parallel_for aşağıdaki gibi bir algoritma.
// Compute whether each point lies in the Mandelbrot set.
parallel_for (0u, height, [&](UINT row)
{
// Loop body omitted for brevity.
});
Her bir bit eşlem öğesi SD bağımsız olduğundan, bitmap belleğe erişim çizim işlemleri eşitleme gerekmez. Bu işlemciler kullanılabilir artar sayısı olarak ölçeklemek performans sağlar.
Top
İptal etmek için destek ekleme
Pencereyi yeniden boyutlandırma tutamacını ve pencere bozulduğunda etkin herhangi bir çizim görevleri iptal Bu bölümde anlatılmaktadır.
Belge ppl iptali iptali çalışma zamanında nasıl çalıştığını açıklar. İşbirliği yapan iptali; Bu nedenle, onu hemen ortaya çıkmaz. İptal edilmiş bir görevi durdurmak için çalışma zamanı bir iç özel durum görev sonraki çağrı sırasında çalışma zamanı içine atar. Önceki bölümde nasıl kullanılacağını gösterir parallel_for çizim görev performansını artırmak için bir algoritma. Arama parallel_for görevi durdurmak, çalışma zamanı etkinleştirir ve bu nedenle çalışmak iptal bilgisi sağlar.
Etkin Görevler iptal ediliyor
Mandelbrot uygulaması oluşturur Bitmap nesneleri, boyutlarla eşleşecek İstemcisi penceresinin boyutu. İstemci penceresi yeniden boyutlandırıldığında her zaman uygulama yeni pencere boyutu için bir görüntü oluşturmak için bir ek arka plan görevi oluşturur. Uygulama, bu ara görüntüleri gerektirmez; Son pencere boyutu için görüntü gerektirir. Bu ek iş gerçekleştirme uygulamanın önlemek için ileti işleyicileri için herhangi bir etkin çizim görevleri iptal edebilirsiniz WM_SIZE ve WM_SIZING iletileri ve sonra yeniden çizim çalışması sonra pencere yeniden boyutlandırılır.
Çağıran uygulama penceresi yeniden boyutlandırıldığında çizim etkin görevleri iptal etmek için concurrency::task_group::cancel için işleyiciler yönteminde WM_SIZING ve WM_SIZE iletileri. İşleyici için WM_SIZE çağrıları da ileti concurrency::task_group::wait yöntemi için tüm etkin görevleri tamamlamak için ve sonra güncelleştirilmiş pencere boyutu için çizim görev reschedules bekleyin.
İstemci pencere bozulduğunda etkin herhangi bir çizim görevleri iptal etmek için iyi bir yöntemdir. Etkin herhangi bir çizim görevleri iptal etme istemci penceresi yok sonra iş parçacıkları UI iş parçacığına ileti göndermeyin emin olur. Uygulama işleyicisi etkin herhangi bir çizim görevleri iptal eder WM_DESTROY ileti.
İptali için yanıt
CChildView::DrawMandelbrot Çizim görev yapar, yöntem iptali için yanıt vermek gerekir. Çalışma zamanı özel durum işleme görevleri iptal etmek için kullandığı CChildView::DrawMandelbrot tüm kaynakların doğru Temizlenen emin garanti etmek için özel durum-güvenli bir mekanizma yöntemi kullanmalıdır. Bu örnek kullanır Olan kaynak alma başlatma (RAII) desen görev iptal ettiğinizde, bit eşlem bit kilidi olmaktır.
Mandelbrot uygulamanın iptali için destek eklemek için
ChildView.h içinde protected bölümünde CChildView sınıfı, bildirimleri için ekleme OnSize, OnSizing, ve OnDestroy ileti eşleme işlevlerini.
afx_msg void OnPaint(); afx_msg void OnSize(UINT, int, int); afx_msg void OnSizing(UINT, LPRECT); afx_msg void OnDestroy(); DECLARE_MESSAGE_MAP()
ChildView.cpp içinde işleyicilerini içeren ileti haritayı değiştirmek WM_SIZE, WM_SIZING, ve WM_DESTROY iletileri.
BEGIN_MESSAGE_MAP(CChildView, CWnd) ON_WM_PAINT() ON_WM_SIZE() ON_WM_SIZING() ON_WM_DESTROY() END_MESSAGE_MAP()
Implement CChildView::OnSizing yöntemi. Bu yöntem, herhangi bir varolan çizim görevleri iptal eder.
void CChildView::OnSizing(UINT nSide, LPRECT lpRect) { // The window size is changing; cancel any existing drawing tasks. m_DrawingTasks.cancel(); }
Implement CChildView::OnSize yöntemi. Bu yöntem, herhangi bir varolan çizim görevleri iptal eder ve Yeni Çizim görev için güncelleştirilmiş istemci pencere boyutunu oluşturur.
void CChildView::OnSize(UINT nType, int cx, int cy) { // The window size has changed; cancel any existing drawing tasks. m_DrawingTasks.cancel(); // Wait for any existing tasks to finish. m_DrawingTasks.wait(); // If the new size is non-zero, create a task to draw the Mandelbrot // image on a separate thread. if (cx != 0 && cy != 0) { m_DrawingTasks.run([cx,cy,this]() { DrawMandelbrot(BitmapPtr(new Bitmap(cx, cy))); }); } }
Implement CChildView::OnDestroy yöntemi. Bu yöntem, herhangi bir varolan çizim görevleri iptal eder.
void CChildView::OnDestroy() { // The window is being destroyed; cancel any existing drawing tasks. m_DrawingTasks.cancel(); // Wait for any existing tasks to finish. m_DrawingTasks.wait(); }
Tanımlamak ChildView.cpp, scope_guard RAII desen uygulayan sınıf.
// Implements the Resource Acquisition Is Initialization (RAII) pattern // by calling the specified function after leaving scope. class scope_guard { public: explicit scope_guard(std::function<void()> f) : m_f(std::move(f)) { } // Dismisses the action. void dismiss() { m_f = nullptr; } ~scope_guard() { // Call the function. if (m_f) { try { m_f(); } catch (...) { terminate(); } } } private: // The function to call when leaving scope. std::function<void()> m_f; // Hide copy constructor and assignment operator. scope_guard(const scope_guard&); scope_guard& operator=(const scope_guard&); };
Aşağıdaki kodu ekleyip CChildView::DrawMandelbrot yöntemi çağrısının Bitmap::LockBits:
// Create a scope_guard object that unlocks the bitmap bits when it // leaves scope. This ensures that the bitmap is properly handled // when the task is canceled. scope_guard guard([&pBitmap, &bitmapData] { // Unlock the bitmap from system memory. pBitmap->UnlockBits(&bitmapData); });
Bu kodu oluşturarak iptali işleme bir scope_guard nesnesi. Nesne kapsamı ayrıldığında, bit eşlem bit kilidini açar.
Sonunda değiştirmek CChildView::DrawMandelbrot kapatmak için yöntem scope_guard bit eşlem bit kilidi sonra ancak UI iş parçacığına iletiler gönderilmeden önce nesne. Bu bit eşlem bit kilidi önce UI iş parçacığı güncelleştirilmez sağlar.
// Unlock the bitmap from system memory. pBitmap->UnlockBits(&bitmapData); // Dismiss the scope guard because the bitmap has been // properly unlocked. guard.dismiss(); // Add the Bitmap object to image queue. send(m_MandelbrotImages, pBitmap); // Post a paint message to the UI thread. PostMessage(WM_PAINT); // Invalidate the client area. InvalidateRect(NULL, FALSE);
Oluşturma ve çalışan uygulama başarıyla güncelleştirildi doğrulayın.
Pencereyi boyutlandırdığınızda çalışma çizim için son pencere boyutu gerçekleştirilir. Pencere bozulduğunda etkin herhangi bir çizim görevleri de iptal edilir.
Top
Ayrıca bkz.
Kavramlar
Görev paralellik (eşzamanlılık çalışma zamanı)