Programmering

Når skal du bruke det flyktige nøkkelordet i C #

Optimaliseringsteknikkene som brukes av JIT (just-in-time) kompilatoren i Common Language Runtime kan føre til uforutsigbare resultater når .Net-programmet prøver å utføre ikke-flyktige avlesninger av data i et flertrådet scenario. I denne artikkelen ser vi på forskjellene mellom flyktig og ikke-flyktig minnetilgang, rollen som det flyktige søkeordet i C #, og hvordan det flyktige søkeordet skal brukes.

Jeg vil gi noen kodeeksempler i C # for å illustrere konseptene. For å forstå hvordan det flyktige nøkkelordet fungerer, må vi først forstå hvordan JIT-kompilatoroptimaliseringsstrategien fungerer i .Net.

Forståelse av JIT-kompilatoroptimaliseringer

Det skal bemerkes at JIT-kompilatoren, som en del av en optimaliseringsstrategi, vil endre rekkefølgen på avlesningene og skrivene på en måte som ikke endrer programmets betydning og eventuelle resultater. Dette er illustrert i kodebiten gitt nedenfor.

x = 0;

x = 1;

Ovennevnte kodebit kan endres til følgende - mens programvarens originale semantikk bevares.

x = 1;

JIT-kompilatoren kan også bruke et konsept som kalles "konstant forplantning" for å optimalisere følgende kode.

x = 1;

y = x;

Ovennevnte kodebit kan endres til følgende - igjen mens den originale semantikken i programmet bevares.

x = 1;

y = 1;

Flyktig kontra ikke-flyktig minnetilgang

Minnemodellen til moderne systemer er ganske komplisert. Du har prosessorregistre, forskjellige nivåer av hurtigbuffere og hovedminne som deles av flere prosessorer. Når programmet kjøres, kan prosessoren cache dataene og deretter få tilgang til disse dataene fra cachen når den blir bedt om av den utførende tråden. Oppdateringer og lesing av disse dataene kan kjøre mot den hurtigbufrede versjonen av dataene, mens hovedminnet oppdateres på et senere tidspunkt. Denne modellen for minnebruk har konsekvenser for flertrådede applikasjoner.

Når en tråd samhandler med dataene i hurtigbufferen, og en annen tråd prøver å lese de samme dataene samtidig, kan den andre tråden lese en utdatert versjon av dataene fra hovedminnet. Dette er fordi når verdien til et ikke-flyktig objekt oppdateres, blir endringen gjort i hurtigbufferen til den utførende tråden og ikke i hovedminnet. Når verdien av et flyktig objekt oppdateres, blir imidlertid ikke bare endringen gjort i hurtigbufferen til den utførende tråden, men denne hurtigbufferen skylles deretter til hovedminnet. Og når verdien til et flyktig objekt blir lest, oppdaterer tråden hurtigbufferen og leser den oppdaterte verdien.

Bruke det flyktige nøkkelordet i C #

Det flyktige nøkkelordet i C # brukes til å informere JIT-kompilatoren om at verdien til variabelen aldri skal caches fordi den kan endres av operativsystemet, maskinvaren eller en tråd som samtidig utføres. Kompilatoren unngår altså å bruke optimaliseringer på variabelen som kan føre til datakonflikter, dvs. til forskjellige tråder som får tilgang til forskjellige verdier av variabelen.

Når du merker et objekt eller en variabel som ustabil, blir den en kandidat for ustabil lesing og skriving. Det skal bemerkes at i C # er alle minneskrivninger ustabile uansett om du skriver data til et ustabilt eller ikke-ustabilt objekt. Uklarheten skjer imidlertid når du leser data. Når du leser data som ikke er ustabile, kan den utførende tråden kanskje ikke alltid få den siste verdien. Hvis objektet er flyktig, får tråden alltid den mest oppdaterte verdien.

Du kan erklære en variabel som flyktig ved å gå foran den med flyktige nøkkelord. Følgende kodebit illustrerer dette.

klasse Program

    {

offentlig flyktig int i;

statisk tomrom Main (streng [] args)

        {

// Skriv koden din her

        }

    }

Du kan bruke flyktige søkeord med alle referanse-, peker- og enumtyper. Du kan også bruke den flyktige modifisereren med byte-, kort-, int-, char-, float- og bool-typer. Det skal bemerkes at lokale variabler ikke kan erklæres som flyktige. Når du angir et referansetypeobjekt som flyktig, er bare pekeren (et 32-biters heltall som peker på stedet i minnet der objektet faktisk er lagret), ustabilt, ikke verdien av forekomsten. En dobbel variabel kan heller ikke være flyktig fordi den er 64 bit større, større enn ordstørrelsen på x86-systemer. Hvis du trenger å lage en dobbel variabel flyktig, bør du pakke den inn i klassen. Du kan gjøre dette enkelt ved å opprette en wrapper-klasse som vist i kodebiten nedenfor.

offentlig klasse VolatileDoubleDemo

{

private flyktige WrappedVolatileDouble flyktige data;

}

offentlig klasse WrappedVolatileDouble

{

offentlige dobbeltdata {get; sett; }

Vær imidlertid oppmerksom på begrensningen av ovennevnte kodeeksempel. Selv om du ville ha den siste verdien av flyktige data referansepeker, er du ikke garantert den siste verdien av Data eiendom. Arbeidet rundt for dette er å lage WrappedVolatileDouble type uforanderlig.

Selv om det flyktige nøkkelordet kan hjelpe deg med trådsikkerhet i visse situasjoner, er det ikke en løsning på alle trådproblemene dine. Du bør vite at å merke en variabel eller et objekt som flyktig, ikke betyr at du ikke trenger å bruke låseordet. Det flyktige nøkkelordet er ikke en erstatning for låsenøkkelordet. Det er bare der for å hjelpe deg med å unngå datakonflikter når du har flere tråder som prøver å få tilgang til de samme dataene.

$config[zx-auto] not found$config[zx-overlay] not found