Tady m∙₧ete stßhnout ukßzkovou aplikaci.
V minulΘm dφle tohoto Φlßnku jsem vßm ukßzal jeden zp∙sob jak zrychlit animaci ve vaÜφ aplikaci. Od tΘ doby m∞ napadl jeÜt∞ jeden. Ten sice nedosahuje takovΘ frekvence p°ekreslovßnφ jako p°epsßnφ funkce Run, ale Φasto nßm dostaΦuje. Tento zp∙sob vyu₧φvß pracovnφho vlßkna ve kterΘm se vyvolßvß p°ekreslenφ. Nßsleduje porovnßnφ t∞chto t°φ metod.
Metoda | PoΦet cykl∙ | Relativnφ rychlost v∙Φi timeru |
Timer | 52120 na 1000 p°ekreslenφ | 1 |
Vlßkno (zprßva) | 18950 na 1000 p°ekreslenφ | 2,75 |
Vlßkno (funkce) | 3240 na 100 000 p°ekreslenφ | 1608 |
Run | 1640 na 100 000 p°ekreslenφ | 3178 |
Jak vidφte, pou₧itφm vlßkna a zprßvy nezφskßme takovΘ obrovskΘ zrychlenφ jako p°epsßnφm funkce Run, ale velice Φasto nßm dosa₧enΘ zrychlenφ dostaΦuje. A je mnohem jednoduÜÜφ pro ka₧dΘ okno vytvo°it vlßkno ne₧ upravovat funkci Run. A pokud ve vlßkn∞ volßme p°φmo kreslφcφ funci je rychlost s Run srovnatelnß.
A te∩ jak se to provßdφ. Nejd°φve vlßkno se zprßvou.
Nadefinujte si funkci vlßkna. Nap°φklad takto:
UINT RedrawThread(LPVOID pParam)
{
HWND hWnd = (HWND)pParam; // zkonvertujeme parametr zpßtky na HWND
while (IsWindow(hWnd)) // dokud okno existuje
{
::SendMessage(hWnd,WM_USER+1,0,0); // poÜleme zprßvu, ₧e chceme p°ekreslit a Φekßme dokud na ni okno nezareaguje
}
return 0; // Okno u₧ neexistuje, m∙₧eme ukonΦit vlßkno
}
Te∩ vlßkno musφme spustit a to nejlΘpe ve funkci OnCreate (reakce na zprßvu WM_CREATE).
...
AfxBeginThread(RedrawThread,(LPVOID)m_hWnd,THREAD_PRIORITY_NORMAL);
...
A jeÜt∞ musφme odchytit zprßvu (WM_USER+1) po₧adujφcφ p°ekreslenφ a p°ekreslit okno. K tomu m∙₧eme vyu₧φt mapu zprßv a nebo p°φmo proceduru okna (virtußlnφ funkci WindowProc).
LRESULT CTstView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// TODO: Add your specialized code here and/or call the base class
switch (message)
{
case WM_USER+1:
Redraw(); // Zavolßme svoji funkci, kterß zp∙sobφ p°ekreslenφ
break;
}
return CView::WindowProc(message, wParam, lParam);
}
Jinß mo₧nost jak vyu₧φt vlßkna je p°edat vlßknu mφsto handle okna p°φmo ukazatel na t°φdu okna a funkci Redraw volat p°φmo z vlßkna. Tφm zφskßme frekvenci srovnatelnou s Run (asi poloviΦnφ), ale ve funkci OnDraw p°i kontrole platnosti ukazatele na dokument v debug m≤du mi tato kontrola nedopadala dob°e --> ASSERT_VALID schazovalo aplikaci. V release m≤du, ale ₧ßdnΘ problΘmy nebyly. A pokud nepou₧φvßte architekturu dokument/pohled, nebo okno nemßte propojeno na dokument, nebudou ₧ßdnΘ problΘmy ani v debug m≤du. (Ve skuteΦnosti staΦφ zakomentß°ovat v OnDraw °ßdek ASSERT_VALID(pDoc);)
Potom jednoduchou ·pravou p°edchozφho k≤du zφskßme podstatn∞ v∞tÜφ rychlost.
P°i spouÜt∞nφ vlßkna p°edßme mφsto handle okna ukazatel na t°φdu okna.
AfxBeginThread(RedrawThread,(LPVOID)this,THREAD_PRIORITY_NORMAL);
a trochu upravφme funkci vlßkna.
UINT RedrawThread(LPVOID pParam)
{
CTstView* pView = (CTstView*)pParam; // zkonvertujeme parametr zpßtky na CTstView*
while (IsWindow(pView->hWnd)) // dokud okno existuje
{
pView->Redraw(); // zavolßme funkci, kterß zp∙sobφ p°ekreslenφ
}
return 0; // Okno u₧ neexistuje, m∙₧eme ukonΦit vlßkno
}
Zprßvu WM_USER+1 u₧ nepou₧φvßme, tak₧e ji nemusφme odchytßvat.
Ale pozor. Tahle metoda mß jeÜt∞ jeden zßdrhel. Proto₧e funkce pro p°ekreslenφ se volß ve vlßkn∞ a tφm se i p°ekreslenφ provßdφ ve vlßkn∞, mohlo by se stßt, ₧e se bude p°ekreslovat jak ve vlßkn∞, tak "souΦasn∞" ve vlastnφ aplikaci a to by mohlo vΘst ke chybnΘmu vykreslenφ. Ale pomoc je jednoduchß. Do t°φdy okna °idßme kritickou sekci a vlßkna synchronizujeme. A to takto.
Do t°φdy naÜeho okna p°idßme prom∞nnou kritickΘ sekce (pot°ebuje hlaviΦkov² soubor afxmt.h):
...
CCriticalSection cs;
...
a na zaΦßtku vykreslovßnφ ji uzamkneme a na konci odemkneme. Nap°φklad takto:
void CTstView::Redraw()
{
cs.Lock(); // Uzamkneme kritickou sekci
CClientDC dc(this); // Zφskßme DC okna
OnDraw(&dc); // Zavolßme funkci OnDraw pro vykreslenφ
cs.Unlock(); // Odemkneme kritickou sekci
}
Tφm zajistφme, ₧e se na obrazovku nebude kreslit souΦasn∞ z naÜeho vlßkna a hlavnφho vlßkna aplikace. (I kdy₧ cs.Lock() a cs.Unlock() by m∞li b²t spφÜe uvnit° funkce OnDraw(...).)