Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Pokud vyvíjíte aplikace Direct2D, možná budete muset přistupovat k prostředkům Direct2D z více než jednoho vlákna. V jiných případech můžete chtít použít více vláken, abyste získali lepší výkon nebo lepší odezvu (například použití jednoho vlákna pro zobrazení obrazovky a samostatné vlákno pro offline vykreslování).
Toto téma popisuje osvědčené postupy pro vývoj vícevláknových aplikací Direct2D s malými až žádným vykreslováním direct3D. Chyby softwaru způsobené problémy souběžnosti můžou být obtížné sledovat a je užitečné naplánovat zásady multithreadingu a postupovat podle zde popsaných osvědčených postupů.
Poznámka
Pokud přistupujete ke dvěma prostředkům Direct2D vytvořeným ze dvou různých jednovláknových továren Direct2D, nezpůsobí konflikty přístupu, pokud jsou základní zařízení a kontexty zařízení Direct3 D odlišné. Pokud mluvíme o "přístupu k prostředkům Direct2D" v tomto článku, znamená to opravdu "přístup k prostředkům Direct2D vytvořeným ze stejného zařízení Direct2D", pokud není uvedeno jinak.
Vývoj aplikací Thread-Safe, které volají pouze rozhraní API Direct2D
Můžete vytvořit vícevláknovou instanci Direct2D factory. Můžete použít a sdílet vícevláknovou továrnu a všechny její prostředky z více než jednoho vlákna, ale přístup k těmto prostředkům (prostřednictvím volání Direct2D) jsou serializovány direct2D, takže nedojde ke konfliktům přístupu. Pokud vaše aplikace volá jenom rozhraní API Direct2D, tato ochrana se automaticky provádí pomocí Direct2D v podrobné úrovni s minimální režií. Kód, který tady vytvoří vícevláknovou továrnu.
ID2D1Factory* m_D2DFactory;
// Create a Direct2D factory.
HRESULT hr = D2D1CreateFactory(
D2D1_FACTORY_TYPE_MULTI_THREADED,
&m_D2DFactory
);
Obrázek zde ukazuje, jak Direct2D serializuje dvě vlákna, která provádí volání pouze pomocí rozhraní API Direct2D.
Vývoj Thread-Safe aplikací Direct2D s minimálními voláními Direct3D nebo DXGI
Je to více než často, že aplikace Direct2D také provádí některá volání Direct3D nebo DXGI. Například vlákno zobrazení bude kreslit v Direct2D pak prezentovat pomocí DXGI swap chain.
V tomto případě je zajištění bezpečnosti vláken složitější: některé Direct2D volání nepřímo přistupují k podkladovým prostředkům Direct3D prostředků, ke kterým může současně přistupovat jiné vlákno, které volá Direct3D nebo DXGI. Vzhledem k tomu, že volání Direct3D nebo DXGI jsou mimo povědomí a kontrolu Direct2D, musíte vytvořit vícevláknovou továrnu Direct2D, ale musíte udělat mor, abyste zabránili konfliktům přístupu.
Tento diagram znázorňuje konflikt přístupu k prostředkům Direct3D kvůli tomu, že vlákno T0 přistupuje k prostředku nepřímo prostřednictvím direct2D volání a T2 přistupující ke stejnému prostředku přímo přes volání Direct3D nebo DXGI.
Poznámka
Ochrana vlákna, která Direct2D poskytuje (modrý zámek na tomto obrázku), v tomto případě nepomůže.
Pokud se chcete vyhnout konfliktu přístupu k prostředkům, doporučujeme explicitně získat zámek, který Direct2D používá pro interní synchronizaci přístupu, a tento zámek použít v případě, že vlákno potřebuje provést volání Direct3D nebo DXGI, které by mohly způsobit konflikt přístupu, jak je znázorněno zde. Konkrétně byste měli věnovat zvláštní pozornost kódu, který používá výjimky nebo systém s včasným ukončením na základě návratových kódů HRESULT. Z tohoto důvodu doporučujeme použít vzor RAII (Získání prostředků inicializace) k volání metod Enter a Leave.
Poznámka
Je důležité spárovat volání Enter a Opustit metody, jinak může vaše aplikace zablokování.
Tento kód ukazuje příklad, kdy uzamknout a pak odemknout kolem volání Direct3D nebo DXGI.
void MyApp::DrawFromThread2()
{
// We are accessing Direct3D resources directly without Direct2D's knowledge, so we
// must manually acquire and apply the Direct2D factory lock.
ID2D1Multithread* m_D2DMultithread;
m_D2DFactory->QueryInterface(IID_PPV_ARGS(&m_D2DMultithread));
m_D2DMultithread->Enter();
// Now it is safe to make Direct3D/DXGI calls, such as IDXGISwapChain::Present
MakeDirect3DCalls();
// It is absolutely critical that the factory lock be released upon
// exiting this function, or else any consequent Direct2D calls will be blocked.
m_D2DMultithread->Leave();
}
Poznámka
Některé volání Direct3D nebo DXGI (zejména IDXGISwapChain::P resent) mohou získat zámky a/nebo aktivovat zpětná volání do kódu volající funkce nebo metody. Měli byste o tom vědět a ujistit se, že takové chování nezpůsobí zablokování. Další informace najdete v tématu přehledu DXGI.
Když použijete metody Enter a Opustit, budou volání chráněná automatickým direct2D a explicitním zámkem, takže aplikace nenarazí na konflikt přístupu.
Existují další přístupy k řešení tohoto problému. Doporučujeme ale explicitně chránit volání Direct3D nebo DXGI pomocí zámku Direct2D, protože obvykle poskytuje lepší výkon, protože chrání souběžnost na mnohem jemné úrovni a s nižší režií pod krytem Direct2D.
Zajištění atomicity stavových operací
I když funkce zabezpečení vláken DirectX můžou pomoct zajistit, že se souběžně neprovedou žádná dvě jednotlivá volání rozhraní API, musíte také zajistit, aby vlákna, která provádějí stavová volání rozhraní API, vzájemně nepřerušovala. Tady je příklad.
- Existují dva řádky textu, které chcete vykreslit na obrazovce (podle vlákna 0) i mimo obrazovku (podle vlákna 1): Řádek č. 1 je "A je větší" a řádek č. 2 je "než B", z nichž obě budou kresleny pomocí plného černého štětce.
- Vlákno 1 nakreslí první řádek textu.
- Vlákno 0 reaguje na uživatelský vstup, aktualizuje jak textové řádky na "B je menší", tak "než A" a změnila barvu štětce na plnou červenou pro vlastní kresbu;
- Vlákno 1 pokračuje ve kreslení druhého řádku textu, který je nyní "než A", s červeným barevným štětcem;
- Nakonec získáme dva řádky textu na cíli kreslení mimo obrazovku: "A je větší" černobíle a "než A" červeně.
V horním řádku nakreslí vlákno 0 s aktuálními textovými řetězci a aktuálním černým štětcem. Vlákno 1 dokončí pouze kreslení mimo obrazovku v horní polovině.
Na prostředním řádku reaguje vlákno 0 na interakci uživatele, aktualizuje textové řetězce a štětec a pak aktualizuje obrazovku. V tomto okamžiku je vlákno 1 blokováno. Poslední vykreslení mimo obrazovku v dolním řádku po vlákně 1 pokračuje ve kreslení dolní poloviny s upraveným štětcem a upraveným textovým řetězcem.
Pokud chcete tento problém vyřešit, doporučujeme mít pro každé vlákno samostatný kontext, aby:
- Měli byste vytvořit kopii kontextu zařízení, aby se při vykreslení nezměnily proměnlivé prostředky (tj. prostředky, které se můžou při zobrazení nebo tisku lišit, například obsah textu nebo štětec plné barvy v příkladu). V této ukázce byste měli před kreslením zachovat kopii těchto dvou řádků textu a barevného štětce. Tím zaručujete, že každé vlákno má kompletní a konzistentní obsah pro kreslení a prezentaci.
- Měli byste sdílet prostředky s velkou hmotností (například rastrové obrázky a grafy složitých efektů), které se inicializují jednou a pak se nikdy neupravují napříč vlákny, aby se zvýšil výkon.
- Můžete buď sdílet zdroje s lehkou hmotností (například pevné barevné štětce a formáty textu), které se inicializují jednou a pak se nikdy nezmění napříč vlákny, nebo ne.
Shrnutí
Při vývoji vícevláknových aplikací Direct2D je nutné vytvořit vícevláknovou továrnu Direct2D a potom z této továrny odvodit všechny prostředky Direct2D. Pokud vlákno provede volání Direct3D nebo DXGI, musíte také explicitně získat zámek Direct2D pro ochranu těchto volání Direct3D nebo DXGI. Navíc musíte zajistit integritu kontextu tím, že pro každé vlákno máte kopii proměnlivých prostředků.