Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Varning
Dödlägesrisk med asynkronisering/inväntning och blockering av anrop på STA-trådar: I hanterad kod (.NET) är STA-meddelandeloopen viktig för COM-anropssändning. Blockera en STA-tråd med Task.Wait(), Task.Result, Thread.Sleep()eller ManualResetEvent.WaitOne() förhindrar COM-återanrop och samtal mellan lägenheter från att slutföras – vilket orsakar ett dödläge.
// ❌ DEADLOCK — blocks the STA message loop
[STAThread]
static void Main()
{
var result = GetDataAsync().Result; // Deadlock: awaited continuation
// can't post back to this STA thread
}
// ✅ Correct — use async entry point or pump messages
[STAThread]
static async Task Main()
{
var result = await GetDataAsync(); // continuation resumes on STA via SynchronizationContext
}
Om du inte kan använda await, använd CoWaitForMultipleHandles (inbyggd) eller en dispatcher-ram/meddelandeloop (hanterad) i stället för råa vänteprimitiver på STA-trådar.
Att använda entrådade lägenheter (lägenhetsmodellprocessen) erbjuder ett meddelandebaserat paradigm för att hantera flera objekt som körs samtidigt. Det gör att du kan skriva effektivare kod genom att låta en tråd, medan den väntar på att en viss tidskrävande åtgärd ska slutföras, tillåta att en annan tråd körs.
Varje tråd i en process som initieras som en lägenhetsmodellprocess, och som hämtar och skickar fönstermeddelanden, är en enkeltrådad lägenhetstråd. Varje tråd bor i sin egen lägenhet. I en lägenhet kan gränssnittspekare skickas utan marskalkning, och därför kommunicerar alla objekt i en enda trådad lägenhetstråd direkt.
En logisk gruppering av relaterade objekt som alla körs på samma tråd, och därför måste ha synkron körning, kan finnas i samma entrådade lägenhetstråd. Ett lägenhetsmodellobjekt kan dock inte finnas i mer än en tråd. Anrop till objekt i andra trådar måste göras i kontexten för den ägande tråden, så distribuerade COM växlar trådar åt dig automatiskt när du anropar en proxy.
Modellerna interprocess och interthread liknar varandra. När det är nödvändigt att skicka en gränssnittspekare till ett objekt i en annan lägenhet (på en annan tråd) inom samma process använder du samma marskalkeringsmodell som objekt i olika processer använder för att skicka pekare över processgränser. Genom att hämta en pekare till standardmarheringsobjektet kan du konvertera gränssnittspekare över trådgränser (mellan lägenheter) på samma sätt som du gör mellan processerna. (Gränssnittspekare måste konverteras när de skickas mellan lägenheter.)
Regler för entrådade lägenheter är enkla, men det är viktigt att följa dem noggrant:
- Varje objekt ska bara finnas på en tråd (inom en entrådad lägenhet).
- Initiera COM-biblioteket för varje tråd.
- Konvertera alla pekare till objekt när du skickar dem mellan lägenheter.
- Varje enskild trådad lägenhet måste ha en meddelandeloop för att hantera anrop från andra processer och lägenheter inom samma process. Entrådade lägenheter utan objekt (endast klient) behöver också en meddelandeloop för att skicka de sändningsmeddelanden som vissa program använder.
- DLL-baserade eller processbaserade objekt anropar inte COM-initieringsfunktionerna. I stället registrerar de sin trådningsmodell med ThreadingModel namngivet värde under InprocServer32 nyckel i registret. Lägenhetsmedvetna objekt måste också skriva DLL-startpunkter noggrant. Det finns särskilda överväganden som gäller för trådning av processerver. Mer information finns i In-Process servertrådsproblem.
Även om flera objekt kan leva på en enda tråd, kan inget lägenhetsmodellobjekt leva på mer än en tråd.
Varje tråd i en klientprocess eller en out-of-process-server måste anropa CoInitializeeller anropa CoInitializeEx och ange COINIT_APARTMENTTHREADED för parametern dwCoInit. Huvudapartmentet är den tråd som först anropar CoInitializeEx. Information om servrar i processen finns i Trådningsproblem för servrar i processen.
Alla anrop till ett objekt måste göras på dess tråd (i lägenheten). Det är förbjudet att anropa ett objekt direkt från en annan tråd. om du använder objekt på det här fria sättet kan det orsaka problem för program. Konsekvensen av den här regeln är att alla pekare på objekt måste konverteras när de skickas mellan lägenheter. COM tillhandahåller följande två funktioner för detta ändamål:
- CoMarshalInterThreadInterfaceInStream konverterar ett gränssnitt till ett dataströmobjekt som returneras till anroparen.
- CoGetInterfaceAndReleaseStream tar bort en gränssnittspekare från ett strömobjekt och släpper den.
Dessa funktioner omsluter anrop till funktionerna CoMarshalInterface och CoUnmarshalInterface, vilket kräver att flaggan MSHCTX_INPROC används.
I allmänhet utförs marskalkeringen automatiskt av COM. När du till exempel skickar en gränssnittspekare som en parameter i ett metodanrop på en proxy till ett objekt i en annan lägenhet, eller när du anropar CoCreateInstance, utför COM marskalkeringen automatiskt. Men i vissa specialfall, där programskrivaren skickar gränssnittspekare mellan lägenheter utan att använda de normala COM-mekanismerna, måste författaren hantera marskalkningen manuellt.
Om en apartment (Apartment 1) i en process har en gränssnittspekare och en annan apartment (Apartment 2) behöver använda den, måste Apartment 1 anropa CoMarshalInterThreadInterfaceInStream för att marshala gränssnittet. Strömmen som skapas av den här funktionen är trådsäker och måste lagras i en variabel som kan nås av Lägenhet 2. Apartment 2 måste skicka den här dataströmmen till CoGetInterfaceAndReleaseStream för att avserialisera gränssnittet och får tillbaka en pekare till en proxy, via vilken den kan komma åt gränssnittet. Huvud-apartmentet måste förbli aktivt tills klienten har slutfört allt COM-arbete (eftersom vissa objekt i processen läses in i huvud-apartmentet, som beskrivs i In-Process Server Threading Issues). När ett objekt har skickats mellan trådar på det här sättet är det mycket enkelt att skicka gränssnittspekare som parametrar. På så sätt utför distribuerad COM marshaling och trådväxling åt programmet.
För att hantera anrop från andra processer och lägenheter inom samma process måste varje enskild trådad lägenhet ha en meddelandeloop. Det innebär att trådens arbetsfunktion måste ha en GetMessage/DispatchMessage-loop. Om andra synkroniseringsprimärer används för att kommunicera mellan trådar kan funktionen MsgWaitForMultipleObjects användas för att vänta både på meddelanden och för trådsynkroniseringshändelser. Dokumentationen för den här funktionen innehåller ett exempel på den här typen av kombinationsloop.
COM skapar ett dolt fönster med windowsklassen "OleMainThreadWndClass" i varje enskild trådad lägenhet. Ett anrop till ett objekt tas emot som ett fönstermeddelande till det här dolda fönstret. När objektets lägenhet hämtar och skickar meddelandet kommer det dolda fönstret att ta emot det. Fönsterproceduren anropar sedan motsvarande gränssnittsmetod för objektet.
När flera klienter anropar ett objekt placeras anropen i kö i meddelandekön och objektet får ett samtal varje gång lägenheten hämtar och skickar meddelanden. Eftersom anropen synkroniseras av COM och anropen alltid levereras av tråden som tillhör objektets lägenhet, behöver objektets gränssnittsimplementeringar inte tillhandahålla synkronisering. Entrådade lägenheter kan implementera IMessageFilter så att de kan avbryta samtal eller ta emot fönstermeddelanden vid behov.
Objektet kan anropas på nytt om någon av dess implementeringar av gränssnittsmetoder hämtar och skickar meddelanden eller gör ett ORPC-anrop till en annan tråd, vilket leder till att ytterligare ett anrop levereras till objektet (av samma trådgrupp). OLE förhindrar inte återinträde i samma tråd, men det kan bidra till trådsäkerhet. Detta är identiskt med det sätt som en fönsterprocedur kan återinträda på om den hämtar och skickar meddelanden medan den bearbetar ett meddelande. Men om du anropar en server utanför processen i en entrådad apartment som i sin tur anropar en annan server i en entrådad apartment, kan den första servern anropas på nytt innan det första anropet har avslutats.
Relaterade ämnen