Megosztás a következőn keresztül:


Többszálas Direct2D-alkalmazások

Ha Direct2D-alkalmazásokat fejleszt, előfordulhat, hogy több szálról is hozzá kell férnie a Direct2D-erőforrásokhoz. Más esetekben érdemes lehet többszálas megjelenítést használni a jobb teljesítmény vagy jobb válaszképesség érdekében (például egy szál használata képernyőmegjelenítéshez és külön szál offline rendereléshez).

Ez a témakör a többszálú Direct2D-alkalmazások fejlesztésének ajánlott eljárásait ismerteti, amelyekhez kevés a Direct3D renderelése. Az egyidejűségi problémák által okozott szoftverhibákat nehéz lehet nyomon követni, és hasznos a többszálú szabályzat megtervezése és az itt leírt ajánlott eljárások követése.

Jegyzet

Ha két Direct2D két különböző egyszálas Direct2D-gyárból létrehozott erőforráshoz fér hozzá, az nem okoz hozzáférési ütközéseket mindaddig, amíg az alapul szolgáló Direct3D eszközök és eszközkörnyezetek is eltérőek. Ha ebben a cikkben a "Direct2D-erőforrások eléréséről" van szó, az valójában azt jelenti, hogy "hozzáférés az azonos Direct2D-eszközről létrehozott Direct2D-erőforrásokhoz", hacsak másként nem rendelkezik.

Csak Direct2D API-kat hívó Thread-Safe-alkalmazások fejlesztése

Létrehozhat többszálú Direct2D gyári példányt. Többszálas gyárat és annak összes erőforrását több szálból is használhatja és megoszthatja, de ezekhez az erőforrásokhoz való hozzáféréseket (Direct2D-hívásokon keresztül) a Direct2D szerializálja, így nem fordul elő hozzáférési ütközés. Ha az alkalmazás csak Direct2D API-kat hív meg, az ilyen védelmet a Direct2D automatikusan, részletes szinten, minimális többletterheléssel végzi el. A többszálú gyár létrehozásához itt található kód.

ID2D1Factory* m_D2DFactory;

// Create a Direct2D factory.
HRESULT hr = D2D1CreateFactory(
    D2D1_FACTORY_TYPE_MULTI_THREADED,
    &m_D2DFactory
);

Az alábbi képen látható, hogy Direct2D hogyan szerializál két szálat, amelyek csak a Direct2D API használatával kezdeményeznek hívásokat.

két szerializált szál diagramja.

Thread-Safe Direct2D-alkalmazások fejlesztése minimális Direct3D- vagy DXGI-hívásokkal

Egy Direct2D-alkalmazás több mint gyakran Direct3D- vagy DXGI-hívásokat is indít. Egy megjelenítési szál például a Direct2D-ben fog rajzolni, majd egy DXGI-felcserélési lánchasználatával jelenik meg.

Ebben az esetben a szálbiztonság biztosítása bonyolultabb: egyes Direct2D hívások közvetetten hozzáférnek az alapul szolgáló Direct3D erőforrásokhoz, amelyeket egy másik, Direct3D-t vagy DXGI-t hívó szál érhet el. Mivel ezek a Direct3D- vagy DXGI-hívások nem rendelkeznek a Direct2D tudatosságával és felügyeletével, létre kell hoznia egy többszálú Direct2D-gyárat, de a hozzáférési ütközések elkerülése érdekében el kell végeznie a mort.

Az alábbi ábrán egy Direct3D erőforrás-hozzáférési ütközés látható, mivel a T0 szál egy Direct2D híváson keresztül közvetetten hozzáfér egy erőforráshoz, és a T2 közvetlenül egy Direct3D- vagy DXGI-híváson keresztül éri el ugyanazt az erőforrást.

Jegyzet

A Direct2D által biztosított szálvédelem (a képen látható kék zárolás) ebben az esetben nem segít.

 

szálvédelmi diagram.

Az erőforrás-hozzáférési ütközések elkerülése érdekében javasoljuk, hogy explicit módon szerezze be azt a zárolást, amelyet Direct2D használ a belső hozzáférés-szinkronizáláshoz, és alkalmazza ezt a zárolást, amikor egy szálnak Direct3D vagy DXGI-hívásokat kell végrehajtania, amelyek hozzáférési ütközést okozhatnak az itt látható módon. Különös figyelmet kell fordítani a kivételeket használó kódra, vagy egy HRESULT visszatérési kódokon alapuló korai rendszerre. Ezért javasoljuk, hogy egy RAII (Resource Acquisition Is Initialization) mintát használva hívja meg a Enter és Leave metódusokat.

Jegyzet

Fontos, hogy párosítsa a hívásokat az Enter és Leave metódusokkal, különben az alkalmazás holtpontra léphet.

 

Az alábbi kód egy példát mutat be arra, hogy mikor kell zárolni, majd feloldani Direct3D vagy DXGI-hívásokat.

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

Jegyzet

Egyes Direct3D-- vagy DXGI-hívások (különösen IDXGISwapChain::P resent) zárolhatnak és/vagy visszahívásokat indíthatnak a hívó függvény vagy metódus kódjába. Tisztában kell lennie ezzel, és győződjön meg arról, hogy az ilyen viselkedés nem okoz holtpontot. További információt a DXGI áttekintési témakörében talál.

 

direct2d és direct3d szálzárolási diagramot.

Ha az Enter és Leave metódust használja, a hívásokat az automatikus Direct2D és a explicit zárolás védi, így az alkalmazás nem ütközik a hozzáférési ütközésbe.

A probléma megkerülésére más módszerek is léteznek. Javasoljuk azonban, hogy kifejezetten védje Direct3D vagy DXGI-hívásokat a Direct2D zárolással, mivel ez általában jobb teljesítményt nyújt, mivel sokkal finomabb szinten és alacsonyabb többletterheléssel védi az egyidejűséget a Direct2D fedél alatt.

Az állapotalapú műveletek atomiságának biztosítása

Bár a DirectX szálbiztonsági funkciói segíthetnek abban, hogy egyidejűleg ne történjen két egyedi API-hívás, azt is biztosítania kell, hogy az állapotalapú API-hívásokat kezdeményező szálak ne zavarják egymást. Íme egy példa.

  1. Két sornyi szöveget szeretne megjeleníteni a képernyőn (a 0. szál szerint) és a képernyőn kívül (az 1. szál szerint): Az 1. sor "A nagyobb" és a 2. sor a "B", mindkettőt egy fekete kefével rajzolja meg.
  2. Az 1. szál a szöveg első sorát rajzolja meg.
  3. A 0. szál reagál a felhasználói bemenetre, mindkét szövegsort a "B kisebb" és az "A" értékre frissíti, és a saját rajzához az ecset színét egyszínűre változtatta;
  4. Az 1. szál folytatja a második szövegsor rajzolását, amely most már "A", piros színű ecsettel;
  5. Végül két sornyi szöveget kapunk a képernyőn kívüli rajzcélon: "A nagyobb" fekete és "mint A" pirossal.

a képernyőszálak be- és kikapcsolása diagramját.

A felső sorban a 0. szál az aktuális szöveges sztringekkel és az aktuális fekete ecsettel rajzol. Az 1. szál csak a képernyőn kívüli rajzot fejezi be a felső felében.

A középső sorban a 0. szál válaszol a felhasználói beavatkozásra, frissíti a szöveges sztringeket és az ecsetet, majd frissíti a képernyőt. Ezen a ponton az 1. szál le van tiltva. Az alsó sorban az 1. szál utáni utolsó képernyőn kívüli renderelés egy módosított ecsettel és módosított szöveges sztringgel folytatja az alsó felének rajzolását.

A probléma megoldásához javasoljuk, hogy minden szálhoz külön környezettel rendelkezzen, hogy:

  • Létre kell hoznia egy másolatot az eszközkörnyezetről, hogy a megjelenítendő vagy a nyomtatás során változó erőforrások (például a szöveg tartalma vagy a példában az egyszínű ecset) ne változzon a renderelés során. Ebben a mintában a rajzolás előtt meg kell őriznie a két szövegsor és a színkefe másolatát. Ezzel garantálja, hogy minden szál teljes és konzisztens tartalommal rendelkezik a rajzoláshoz és a bemutatáshoz.
  • A teljesítmény növelése érdekében meg kell osztania a nagy súlyú erőforrásokat (például bitképeket és összetett effektusdiagramokat), amelyeket egyszer inicializál, majd soha nem módosít a szálak között.
  • Megoszthat egyszerű erőforrásokat (például egyszínű ecseteket és szövegformátumokat), amelyeket egyszer inicializál, majd soha nem módosít a szálak között, vagy nem

Összefoglalás

Ha többszálú Direct2D--alkalmazásokat fejleszt, létre kell hoznia egy többszálú Direct2D-gyárat, majd le kell származtatnia az összes Direct2D-erőforrást az adott gyárból. Ha egy szál Direct3D vagy DXGI-hívásokat indít, akkor explicit módon be kell szereznie, majd a Direct2D-zárolást kell alkalmaznia a Direct3D- vagy DXGI-hívások őrzéséhez. Emellett gondoskodnia kell a környezet integritásáról úgy, hogy minden szálhoz rendelkeznie kell egy másolatot a nem módosítható erőforrásokról.