Ajánlott eljárások felügyelt szálkezeléshez

A többszálúság gondos programozást igényel. A legtöbb feladat esetében csökkentheti a bonyolultságot, ha sorba rendezi a végrehajtási kérelmeket a szálkészlet szálai alapján. Ez a témakör a nehezebb helyzetekkel foglalkozik, például több szál munkájának koordinálásával vagy a letiltott szálak kezelésével.

Feljegyzés

A 4. .NET-keretrendszer kezdve a feladat párhuzamos kódtára és a PLINQ olyan API-kat biztosít, amelyek csökkentik a többszálas programozás összetettségének és kockázatainak egy részét. További információ: Párhuzamos programozás a .NET-ben.

Holtpontok és versenyfeltételek

A többszálas megoldás az átviteli sebességgel és a válaszképességgel kapcsolatos problémákat oldja meg, de ezzel új problémákat vezet be: holtpontok és versenyhelyzetek.

Holtpontok

Holtpont akkor fordul elő, ha két szál mindegyike megpróbál zárolni egy erőforrást, amelyet a másik már zárolt. Egyik szál sem tud további haladást elérni.

A felügyelt szálkezelés osztályainak számos módszere időtúllépést biztosít a holtpontok észleléséhez. Az alábbi kód például megpróbál zárolást szerezni egy nevű lockObjectobjektumon. Ha a zárolást nem 300 ezredmásodpercben kapják meg, Monitor.TryEnter akkor a visszaadott falseérték.

If Monitor.TryEnter(lockObject, 300) Then  
    Try  
        ' Place code protected by the Monitor here.  
    Finally  
        Monitor.Exit(lockObject)  
    End Try  
Else  
    ' Code to execute if the attempt times out.  
End If  
if (Monitor.TryEnter(lockObject, 300)) {  
    try {  
        // Place code protected by the Monitor here.  
    }  
    finally {  
        Monitor.Exit(lockObject);  
    }  
}  
else {  
    // Code to execute if the attempt times out.  
}  

Versenyfeltételek

A versenyfeltétel egy olyan hiba, amely akkor fordul elő, ha egy program eredménye attól függ, hogy két vagy több szál közül melyik éri el először egy adott kódblokkot. A program többszöri futtatása különböző eredményeket eredményez, és egy adott futtatás eredménye nem jelezhető előre.

A versenyfeltételek egyszerű példája a mező növekménye. Tegyük fel, hogy egy osztály rendelkezik egy privát statikus mezővel (a Visual Basicben megosztva ), amely az osztály egy példányának létrehozásakor növekszik, például objCt++; a (C#) vagy objCt += 1 a (Visual Basic) kód használatával. Ehhez a művelethez be kell tölteni az értéket egy regiszterbeobjCt, növelni kell az értéket, és el kell őket tárolni.objCt

Többszálas alkalmazásokban előfordulhat, hogy egy olyan szál, amely betöltötte és megnövelte az értéket, egy másik szál előtagja lehet, amely mindhárom lépést végrehajtja; amikor az első szál folytatja a végrehajtást, és tárolja az értékét, felülírja objCt , anélkül, hogy figyelembe veszi, hogy az érték időközben megváltozott.

Ez a faji feltétel könnyen elkerülhető az osztály módszereivel Interlocked , például Interlocked.Increment. A többszálas adatszinkronizálás egyéb technikáiról a Többszálas adatok szinkronizálása című témakörben olvashat.

A versenyfeltételek akkor is előfordulhatnak, ha több szál tevékenységeit szinkronizálja. Amikor kódsort ír, figyelembe kell vennie, hogy mi történhet, ha a sor végrehajtása előtt elő lett készítve egy szál (vagy a sort alkotó egyes gépi utasítások bármelyike), és egy másik szál felülírta azt.

Statikus tagok és statikus konstruktorok

Az osztály nem inicializálódik, amíg az osztálykonstruktor (static c#-ban konstruktor a Shared Sub New Visual Basicben) nem fut. Ha meg szeretné akadályozni a kód nem inicializált típuson történő végrehajtását, a közös nyelvi futtatókörnyezet letiltja az osztálytagok static (Shared Visual Basic-tagok) más szálakból érkező összes hívást, amíg az osztálykonstruktor le nem fut.

Ha például egy osztálykonstruktor új szálat indít el, és a menet eljárása meghívja az osztály egy static tagját, az új szál addig blokkol, amíg az osztálykonstruktor befejeződik.

Ez minden olyan típusra vonatkozik, amely konstruktort static tartalmazhat.

Processzorok száma

A többszálú architektúrára hatással lehet, hogy több processzor vagy csak egy processzor áll rendelkezésre egy rendszeren. További információ: Processzorok száma.

Environment.ProcessorCount A tulajdonság használatával meghatározhatja a futtatáskor elérhető processzorok számát.

Általános javaslatok

Több szál használatakor vegye figyelembe az alábbi irányelveket:

  • Ne használjon Thread.Abort más szálak megszakítására. Egy másik szál meghívása Abort hasonló ahhoz, hogy kivételt dobjon az adott szálra anélkül, hogy tudná, hogy a szál milyen pontot ért el a feldolgozás során.

  • Ne használja Thread.Suspend és Thread.Resume szinkronizálja több szál tevékenységeit. Használja Mutexa , ManualResetEvent, AutoResetEventés Monitor.

  • Ne szabályozza a munkaszálak végrehajtását a fő programból (például események használatával). Ehelyett úgy tervezze meg a programot, hogy a munkaszálak várhassák, amíg a munka elérhetővé válik, végrehajtsa, és ha végzett, értesítse a program más részeit. Ha a munkavégző szálak nem blokkolnak, fontolja meg a szálkészlet-szálak használatát. Monitor.PulseAll olyan helyzetekben hasznos, amikor a munkaszálak blokkolva vannak.

  • Ne használjon típusokat zárolási objektumként. Ez azt jelzi, hogy kerülje az olyan kódokat, mint lock(typeof(X)) a C# vagy SyncLock(GetType(X)) a Visual Basic, vagy az objektumok használata Monitor.EnterType . Egy adott típus esetében alkalmazástartományonként csak egy példány System.Type található. Ha a zárolás típusa nyilvános, a sajáttól eltérő kód zárolhatja azt, ami holtpontokhoz vezethet. További problémákért tekintse meg a megbízhatóság ajánlott eljárásait.

  • Körültekintően zároljon példányokat, például lock(this) C# vagy SyncLock(Me) Visual Basic nyelven. Ha az alkalmazás más, típuson kívüli kódja zárolja az objektumot, holtpontok léphetnek fel.

  • Győződjön meg arról, hogy a figyelőbe belépett szálak mindig elhagyják ezt a figyelőt, még akkor is, ha kivétel történik, miközben a szál a figyelőben van. A C#-zárolási utasítás és a Visual Basic SyncLock utasítás automatikusan biztosítja ezt a viselkedést, és végül egy blokkot alkalmazva biztosítja a Monitor.Exit meghívást. Ha nem tudja biztosítani a kilépés meghívását, fontolja meg a terv módosítását a Mutex használatára. A rendszer automatikusan felszabadít egy mutexet, amikor a jelenleg tulajdonában lévő szál leáll.

  • Használjon több szálat a különböző erőforrásokat igénylő tevékenységekhez, és ne rendeljen több szálat egyetlen erőforráshoz. Az I/O-t érintő bármely feladatnak például előnye, hogy saját szála van, mivel ez a szál blokkolva lesz az I/O-műveletek során, és így lehetővé teszi más szálak végrehajtását. A felhasználói bemenet egy másik erőforrás, amely egy dedikált szál előnyeit élvezi. Az egyprocesszoros számítógépeken a felhasználói bemenettel rendelkező és az I/O-t használó, de több számítási igényű tevékenység is egymással verseng.

  • Fontolja meg az osztály metódusainak használatát az Interlocked egyszerű állapotváltozásokhoz az utasítás használata helyett (SyncLocka lock Visual Basicben). Az lock állítás jó általános célú eszköz, de az Interlocked osztály jobb teljesítményt nyújt az atomi frissítésekhez. Belsőleg egyetlen zárolási előtagot hajt végre, ha nincs versengés. A kódismétlésekben figyelje meg az alábbi példákban láthatóhoz hasonló kódokat. Az első példában egy állapotváltozó növekszik:

    SyncLock lockObject  
        myField += 1  
    End SyncLock  
    
    lock(lockObject)
    {  
        myField++;  
    }  
    

    Az utasítás helyett a metódus használatával javíthatja a Incrementlock teljesítményt az alábbiak szerint:

    System.Threading.Interlocked.Increment(myField)  
    
    System.Threading.Interlocked.Increment(myField);  
    

    Feljegyzés

    Használja a metódust az Add 1-nél nagyobb atomi növekményekhez.

    A második példában a referenciatípus változó csak akkor frissül, ha null értékű hivatkozás (Nothing a Visual Basicben).

    If x Is Nothing Then  
        SyncLock lockObject  
            If x Is Nothing Then  
                x = y  
            End If  
        End SyncLock  
    End If  
    
    if (x == null)  
    {  
        lock (lockObject)  
        {  
            x ??= y;
        }  
    }  
    

    A teljesítmény a módszer használatával CompareExchange javítható, az alábbiak szerint:

    System.Threading.Interlocked.CompareExchange(x, y, Nothing)  
    
    System.Threading.Interlocked.CompareExchange(ref x, y, null);  
    

    Feljegyzés

    A CompareExchange<T>(T, T, T) metódus túlterhelése típusbiztos alternatívát kínál a referenciatípusokhoz.

Javaslatok osztálykódtárakhoz

Többszálú osztálykódtárak tervezésekor vegye figyelembe az alábbi irányelveket:

  • Ha lehetséges, kerülje a szinkronizálás szükségességét. Ez különösen igaz az erősen használt kódokra. Előfordulhat például, hogy egy algoritmus úgy van beállítva, hogy elviseljen egy versenyhelyzetet ahelyett, hogy megszüntetné azt. A szükségtelen szinkronizálás csökkenti a teljesítményt, és holtpontok és versenyfeltételek lehetőségét teremti meg.

  • A statikus adatok (Shared a Visual Basicben) szál biztonságossá tétele alapértelmezés szerint.

  • Alapértelmezés szerint ne tegye biztonságossá a példány adatszálát. A szálbiztos kód létrehozásához zárolt elemek hozzáadása csökkenti a teljesítményt, növeli a zárolási versengést, és lehetővé teszi a holtpontok előfordulását. A gyakori alkalmazásmodellekben egyszerre csak egy szál hajtja végre a felhasználói kódot, ami minimálisra csökkenti a szálbiztonság szükségességét. Ezért a .NET-osztálykódtárak alapértelmezés szerint nem biztonságosak.

  • Kerülje a statikus állapotot módosító statikus metódusok megadását. A gyakori kiszolgálói forgatókönyvekben a statikus állapot meg van osztva a kérelmek között, ami azt jelenti, hogy egyszerre több szál is végrehajthatja ezt a kódot. Ez megnyitja a szálak hibáinak lehetőségét. Érdemes lehet olyan tervezési mintát használni, amely adatokat ágyaz be a kérések között nem megosztott példányokba. Továbbá ha a statikus adatok szinkronizálva vannak, az állapotot módosító statikus metódusok közötti hívások holtpontot vagy redundáns szinkronizálást eredményezhetnek, ami hátrányosan befolyásolhatja a teljesítményt.

Lásd még