Programmering

Hvornår skal du bruge det flygtige nøgleord i C #

Optimeringsteknikkerne, der bruges af JIT (just-in-time) kompilatoren i Common Language Runtime, kan føre til uforudsigelige resultater, når dit .Net-program forsøger at udføre ikke-flygtige aflæsninger af data i et multitrådet scenario. I denne artikel vil vi se på forskellene mellem flygtig og ikke-flygtig hukommelsesadgang, det flygtige søgeords rolle i C #, og hvordan det flygtige søgeord skal bruges.

Jeg vil give nogle kodeeksempler i C # for at illustrere begreberne. For at forstå, hvordan det flygtige nøgleord fungerer, skal vi først forstå, hvordan JIT-kompilatoroptimeringsstrategien fungerer i .Net.

Forståelse af JIT-kompilatoroptimeringer

Det skal bemærkes, at JIT-kompilatoren som en del af en optimeringsstrategi ændrer rækkefølgen af ​​læsningerne og skriver på en måde, der ikke ændrer programmets betydning og eventuelle output. Dette er illustreret i kodestykket nedenfor.

x = 0;

x = 1;

Ovenstående kodestykke kan ændres til følgende - mens programmets originale semantik bevares.

x = 1;

JIT-kompilatoren kan også anvende et koncept kaldet "konstant udbredelse" for at optimere følgende kode.

x = 1;

y = x;

Ovenstående kodestykke kan ændres til følgende - igen, mens den oprindelige semantik i programmet bevares.

x = 1;

y = 1;

Flygtig kontra ikke-flygtig hukommelsesadgang

Hukommelsesmodellen for moderne systemer er ret kompliceret. Du har processorregistre, forskellige niveauer af cacher og hovedhukommelse, der deles af flere processorer. Når dit program udføres, kan processoren cache dataene og derefter få adgang til disse data fra cachen, når den bliver anmodet af den udførende tråd. Opdateringer og læsninger af disse data kan køre mod den cachelagrede version af dataene, mens hovedhukommelsen opdateres på et senere tidspunkt. Denne model af hukommelsesforbrug har konsekvenser for multitrådede applikationer.

Når en tråd interagerer med dataene i cachen, og en anden tråd forsøger at læse de samme data samtidigt, kan den anden tråd muligvis læse en forældet version af dataene fra hovedhukommelsen. Dette skyldes, at når værdien af ​​et ikke-flygtigt objekt opdateres, foretages ændringen i cachen i den udførende tråd og ikke i hovedhukommelsen. Når værdien af ​​et flygtigt objekt opdateres, foretages ikke kun ændringen i cachen i den udførende tråd, men denne cache skylles derefter til hovedhukommelsen. Og når værdien af ​​et flygtigt objekt læses, opdaterer tråden sin cache og læser den opdaterede værdi.

Brug af det flygtige nøgleord i C #

Det flygtige nøgleord i C # bruges til at informere JIT-kompilatoren om, at værdien af ​​variablen aldrig skal caches, fordi den muligvis ændres af operativsystemet, hardwaren eller en samtidig udførende tråd. Compileren undgår således at bruge optimeringer på variablen, der kan føre til datakonflikter, dvs. til forskellige tråde, der får adgang til forskellige værdier af variablen.

Når du markerer et objekt eller en variabel som ustabil, bliver den en kandidat til ustabil læsning og skrivning. Det skal bemærkes, at i C # er alle hukommelseskrivninger flygtige, uanset om du skriver data til et flygtigt eller et ikke-flygtigt objekt. Uklarheden sker dog, når du læser data. Når du læser data, der ikke er flygtige, kan den udførende tråd muligvis ikke altid få den nyeste værdi. Hvis objektet er flygtigt, får tråden altid den mest opdaterede værdi.

Du kan erklære en variabel som flygtig ved at gå foran den med flygtige nøgleord. Følgende kodestykke illustrerer dette.

klasse Program

    {

offentlig flygtig int i;

statisk ugyldigt Main (streng [] args)

        {

// Skriv din kode her

        }

    }

Du kan bruge flygtige nøgleord med enhver reference-, markør- og enumtype. Du kan også bruge den flygtige modifikator med byte-, kort-, int-, char-, float- og bool-typer. Det skal bemærkes, at lokale variabler ikke kan erklæres som flygtige. Når du angiver et referencetypeobjekt som flygtigt, er kun markøren (et 32-bit heltal, der peger på det sted i hukommelsen, hvor objektet faktisk er gemt), ustabilt, ikke værdien af ​​forekomsten. En dobbelt variabel kan heller ikke være flygtig, fordi den er 64 bit større end ordstørrelsen på x86-systemer. Hvis du har brug for at lave en dobbelt variabel flygtig, skal du pakke den inde i klassen. Du kan gøre dette let ved at oprette en indpakningsklasse som vist i kodestykket nedenfor.

offentlig klasse VolatileDoubleDemo

{

private flygtige WrappedVolatileDouble flygtige data;

}

offentlig klasse WrappedVolatileDouble

{

offentlige dobbeltdata {get; sæt; }

Bemærk dog begrænsningen i ovenstående kodeeksempel. Selvom du ville have den nyeste værdi af flygtige data referencemarkør, er du ikke garanteret den nyeste værdi af Data ejendom. Arbejdet rundt med dette er at gøre WrappedVolatileDouble type uforanderlig.

Selv om det flygtige nøgleord kan hjælpe dig med trådsikkerhed i visse situationer, er det ikke en løsning på alle dine trådproblemer. Du skal vide, at markering af en variabel eller et objekt som flygtig ikke betyder, at du ikke behøver at bruge lås nøgleordet. Det flygtige nøgleord er ikke en erstatning for låsenøgleordet. Det er kun der for at hjælpe dig med at undgå datakonflikter, når du har flere tråde, der prøver at få adgang til de samme data.