ã¯ããã«
ãã®èšäºã§ã¯ãéããããã³ã°åæã®å®è£ ã«äœ¿çšãããããã€ãã®èšèšã®äœ¿çšã«ã€ããŠã話ããããšæããŸãã volatileããŒã¯ãŒããVolatileReadãVolatileWriteãããã³MemoryBarrier颿°ã«ã€ããŠèª¬æããŸãã ãããã®èšèªæ§æèŠçŽ ãšãã®è§£æ±ºçã䜿çšããããåŸãªãåé¡ãæ€èšããŸãã ã¡ã¢ãªããªã¢ã«ã€ããŠèª¬æããéã«ã.NETã¡ã¢ãªã¢ãã«ã«ã€ããŠç°¡åã«ç¢ºèªããŸãã
ã³ã³ãã€ã©ãŒã«ãã£ãŠå°å ¥ãããæé©å
ãã³ããããã³ã°åæã䜿çšãããšãã«ããã°ã©ããŒãééããäž»ãªåé¡ã¯ãã³ã³ãã€ã©ãŒã®æé©åãšåœä»€ã®ããã»ããµãŒé åã§ãã
ã³ã³ãã€ã©ãŒããã«ãã¹ã¬ããããã°ã©ã ã«åé¡ãå°å ¥ããäŸãèŠãŠã¿ãŸãããã
class ReorderTest { private int _a; public void Foo() { var task = new Task(Bar); task.Start(); Thread.Sleep(1000); _a = 0; task.Wait(); } public void Bar() { _a = 1; while (_a == 1) { } } }
ãã®äŸãå®è¡ãããšãããã°ã©ã ãããªãŒãºããããšã確èªã§ããŸãã ãã®çç±ã¯ãã³ã³ãã€ã©ãããã»ããµã¬ãžã¹ã¿ã«_a倿°ããã£ãã·ã¥ããããã§ãã
ãã®ãããªåé¡ã解決ããããã«ãCïŒã¯volatileããŒã¯ãŒããæäŸããŠããŸãã ãã®ããŒã¯ãŒãã倿°ã«é©çšãããšãã³ã³ãã€ã©ã¯äœããã®æ¹æ³ã§å€æ°ãžã®ã¢ã¯ã»ã¹ãæé©åã§ããªããªããŸãã
ããã¯ãæ¹èšããã_a倿°å®£èšã®å€èгã§ãã
private volatile int _a;
ãã®ããŒã¯ãŒãã䜿çšããããšã®å¹æã¯ãã³ã³ãã€ã©ã®æé©åãç¡å¹ã«ããããšã ãã§ã¯ãããŸããã ä»ã®å¹æã«ã€ããŠã¯åŸã§èª¬æããŸãã
åœä»€ãäžŠã¹æ¿ãã
åé¡ã®åå ãããã»ããµã«ããåœä»€ã®åé 眮ã§ããå ŽåãèããŠã¿ãŸãããã
次ã®ã³ãŒãããããšããŸãã
class ReorderTest2 { private int _a; private int _b; public void Foo() { _a = 1; _b = 1; } public void Bar() { if (_b == 1) { Console.WriteLine(_a); } } }
Fooã«ãŒãã³ãšBarã«ãŒãã³ã¯ãç°ãªãã¹ã¬ããããåæã«å®è¡ãããŸãã
ãã®ã³ãŒãã¯æ£ããã§ãããã€ãŸããããã°ã©ã ããŒããåºåããªããšèªä¿¡ãæã£ãŠèšããŸããïŒ ã·ã³ã°ã«ã¹ã¬ããã®ããã°ã©ã ã«ã€ããŠè©±ããŠããå Žåããã®ã³ãŒãã確èªããã«ã¯äžåºŠå®è¡ããã ãã§ååã§ãã ãããããã«ãã¹ã¬ãããæ±ã£ãŠãããããããã ãã§ã¯ååã§ã¯ãããŸããã 代ããã«ãããã°ã©ã ãæ£ããåäœããããšãä¿èšŒããŠãããã©ãããçè§£ããå¿ èŠããããŸãã
.NETã¡ã¢ãªã¢ãã«
æ¢ã«è¿°ã¹ãããã«ããã«ãã¹ã¬ããããã°ã©ã ã®äžé©åãªåäœã¯ãããã»ããµäžã®åœä»€ã®äžŠã¹æ¿ãã«ãã£ãŠçºçããå¯èœæ§ããããŸãã ãã®åé¡ãããã«è©³ããèããŠã¿ãŸãããã
ææ°ã®ããã»ããµã¯ãæé©åã®ããã«ã¡ã¢ãªã®èªã¿åãããã³æžã蟌ã¿åœä»€ãåé 眮ã§ããŸãã ãããäŸã§èª¬æããŸãã
int a = _a; _b = 10;
ãã®ã³ãŒãã§ã¯ã倿°_aãæåã«èªã¿åãããæ¬¡ã«_bãæžã蟌ãŸããŸãã ãã ãããã®ããã°ã©ã ãå®è¡ãããšãããã»ããµã¯èªã¿åãããã³æžã蟌ã¿åœä»€ãåé 眮ã§ããŸããã€ãŸãã倿°_bãæåã«æžã蟌ãŸãããã®åŸã§ã®ã¿_aãèªã¿åãããŸãã ã·ã³ã°ã«ã¹ã¬ããããã°ã©ã ã®å Žåããã®é åã¯éèŠã§ã¯ãããŸãããããã«ãã¹ã¬ããããã°ã©ã ã®å Žåãããã¯åé¡ã«ãªããŸãã ããã§ãé åããŠã³ããŒã-ã¬ã³ãŒãã調ã¹ãŸããã ä»ã®åœä»€ã®çµã¿åããã§ãåæ§ã®é åãå¯èœã§ãã
ãã®ãããªåœä»€ã®é åèŠåã®ã»ããã¯ãã¡ã¢ãªã¢ãã«ãšåŒã°ããŸãã .NETãã©ãããã©ãŒã ã«ã¯ç¬èªã®ã¡ã¢ãªã¢ãã«ããããç¹å®ã®ããã»ããµã®ã¡ã¢ãªã¢ãã«ããç§ãã¡ãæœè±¡åããŸãã
ããã¯ã.NETã¡ã¢ãªã¢ãã«ã®å€èгã§ãã
é åã¿ã€ã | èš±å¯ãããé å |
ããŠã³ããŒã-ããŠã³ããŒã | ã¯ã |
ã¬ã³ãŒããããŠã³ããŒã | ã¯ã |
é²é³ããŠã³ããŒã | ã¯ã |
ã¬ã³ãŒã | ãã |
.NETã¡ã¢ãªã¢ãã«ã®èгç¹ããäŸãæ€èšããããšãã§ããŸãã é åã®æžã蟌ã¿/æžã蟌ã¿ãçŠæ¢ãããŠããããã倿°_aãžã®æžã蟌ã¿ã¯åžžã«å€æ°_bãžã®æžã蟌ã¿ã®åã«è¡ãããããã§ããã°ã©ã ã¯æ£ããåäœããŸãã åé¡ã¯ãBarããã·ãŒãžã£ã«ãããŸãã èªã¿åãåœä»€ã®ã¹ã¯ããã¯çŠæ¢ãããŠããªãããã_aã®åã«_b倿°ãèªã¿åãããšãã§ããŸãã
眮æåŸãã³ãŒãã¯æ¬¡ã®ããã«èšè¿°ãããŠãããã®ããã«å®è¡ãããŸãã
var tmp = _a; if (_b == 1) { Console.WriteLine(tmp); }
åœä»€ã®é åã«ã€ããŠè©±ããšããç°ãªã倿°ãèªã¿æžããã1ã€ã®ã¹ã¬ããããã®åœä»€ã®é åãæå³ããŸãã ç°ãªãã¹ã¬ããã§åã倿°ã«ã¬ã³ãŒããããå Žåããããã®é åºã¯ãããã«ããŠãã©ã³ãã ã§ãã ãããŠãããšãã°æ¬¡ã®ããã«ãåã倿°ã®èªã¿åããšæžã蟌ã¿ã«ã€ããŠè©±ããŠããå ŽåïŒ
var a = GetA(); UseA(a);
ãããŠããã¡ãããããã§ã¯é åã¯ããåŸãªãã
èšæ¶ã®éå£
ãã®åé¡ã解決ããã«ã¯ãæ®éçãªæ¹æ³-ã¡ã¢ãªããªã¢ïŒã¡ã¢ãªããªã¢ïŒã远å ããŸãã
ã¡ã¢ãªããªã¢ã«ã¯ããã«ããªãªãŒã¹ãã§ã³ã¹ãã¢ãã¥ã¢ãã§ã³ã¹ã®ã¿ã€ãããããŸãã
å®å šãªããªã¢ã¯ãããªã¢ã®ååŸã«ãããã¹ãŠã®èªã¿åããšæžã蟌ã¿ãããªã¢ã®ååŸã§åãããã«å®è¡ãããããšãä¿èšŒããŸããã€ãŸããã¡ã¢ãªã¢ã¯ã»ã¹åœä»€ãããªã¢ãé£ã³è¶ããããšã¯ã§ããŸããã
次ã«ãä»ã®2ã€ã®ã¿ã€ãã®ããªã¢ãæ±ããŸãã
Accureãã§ã³ã¹ã¯ãããªã¢ã«ç¶ãæç€ºãããªã¢ã®åã®äœçœ®ã«ç§»åããªãããã«ããŸãã
ãªãªãŒã¹ãã§ã³ã¹ã¯ãããªã¢ã®åã®æç€ºãããªã¢ã®åŸã®äœçœ®ã«ç§»åããªãããã«ããŸãã
çšèªã«é¢ããããã€ãã®èšèã æ®çºæ§æžã蟌ã¿ãšããçšèªã¯ããªãªãŒã¹ãã§ã³ã¹ã®äœæãšäœµããŠã¡ã¢ãªã«æžã蟌ãããšãæå³ããŸãã æ®çºæ§èªã¿åããšããçšèªã¯ãã¢ãã¥ã¢ãã§ã³ã¹ã®äœæãšäœµããŠã¡ã¢ãªãèªã¿åãããšãæå³ããŸãã
.NETã¯ãã¡ã¢ãªããªã¢ãæäœããããã«æ¬¡ã®ã¡ãœãããæäŸããŸãã
- Thread.MemoryBarrierïŒïŒã¡ãœããã¯å®å šãªã¡ã¢ãªããªã¢ãäœæããŸã
- volatileããŒã¯ãŒãã¯ããã®åèªã§ããŒã¯ããã倿°ã®ãã¹ãŠã®æäœããããããvolatileæžã蟌ã¿ãŸãã¯volatileèªã¿åãã«å€æããŸãã
- Thread.VolatileReadïŒïŒã¡ãœããã¯æ®çºæ§èªã¿åããå®è¡ããŸã
- ã¡ãœããThread.VolatileWriteïŒïŒã¯ãæ®çºæ§æžã蟌ã¿ãå®è¡ããŸã
äŸã«æ»ããŸãããã ãã§ã«çè§£ããããã«ãèªã¿åãåœä»€ã®é åã«ããåé¡ãçºçããå ŽåããããŸãã ããã解決ããã«ã¯ãèªã¿åã_aãš_bã®éã«ã¡ã¢ãªããªã¢ã远å ããŸãã ãã®åŸãBarã¡ãœãããå®è¡ãããã¹ã¬ããããšã³ããªãæ£ããé åºã§èŠãããšãä¿èšŒãããŸãã
class ReorderTest2 { private int _a; private int _b; public void Foo() { _a = 1; _b = 1; } public void Bar() { if (_a == 1) { Thread.MemoryBarrier(); Console.WriteLine(_b); } } }
ããã§ã¯ãå®å šãªã¡ã¢ãªããªã¢ã䜿çšããããšã¯åé·ã§ãã èªã¿åãåœä»€ã®é åãæé€ããã«ã¯ã_aãèªã¿åããšãã«volatile readã䜿çšããã ãã§ååã§ãã ããã¯ãThread.VolatileReadã¡ãœãããŸãã¯volatileããŒã¯ãŒãã䜿çšããŠå®çŸã§ããŸãã
Thread.VolatileWriteããã³Thread.VolatileReadã¡ãœãã
Thread.VolatileWriteã¡ãœãããšThread.VolatileReadã¡ãœããã詳ããèŠãŠã¿ãŸãããã
VolatileWriteã«é¢ããMSDNã«ã¯ããå€ããã£ãŒã«ãã«çŽæ¥æžã蟌ããããã³ã³ãã¥ãŒã¿ãŒã®ãã¹ãŠã®ããã»ããµããèŠããããã«ãªããŸãããšèšè¿°ãããŠããŸãã
å®éããã®èª¬æã¯å®å šã«æ£ããããã§ã¯ãããŸããã ãããã®ã¡ãœããã¯2ã€ã®ããšãä¿èšŒããŸããã³ã³ãã€ã©1ã®æé©åãšãæ®çºæ§ã®èªã¿åããŸãã¯æžã蟌ã¿ããããã£ã«ããåœä»€ã®äžŠã¹æ¿ãã¯ãããŸããã å³å¯ã«èšãã°ãVolatileWriteã¡ãœããã¯ãå€ãä»ã®ããã»ããµãŒããããã«èŠããããšãä¿èšŒããŸããããŸããVolatileReadã¡ãœããã¯ãå€ããã£ãã·ã¥2ããèªã¿åãããªãããšãä¿èšŒããŸããã ãã ããã³ã³ãã€ã©ã«ããã³ãŒãã®æé©åã®æ¬ åŠãšããã»ããµãã£ãã·ã¥ã®äžè²«æ§ã®ãããMSDNã®èª¬æãæ£ãããšä»®å®ã§ããŸãã
ãããã®ã¡ãœããã®å®è£ æ¹æ³ãæ€èšããŠãã ããã
[MethodImpl(MethodImplOptions.NoInlining)] public static int VolatileRead(ref int address) { int num = address; Thread.MemoryBarrier(); return num; } [MethodImpl(MethodImplOptions.NoInlining)] public static void VolatileWrite(ref int address, int value) { Thread.MemoryBarrier(); address = value; }
ããã§ä»ã«é¢çœããã®ã¯äœã§ããïŒ
ãŸããå®å šãªã¡ã¢ãªããªã¢ã䜿çšããŸãã å ã»ã©èšã£ãããã«ãvolatileæžã蟌ã¿ã¯ãªãªãŒã¹ãã§ã³ã¹ãäœæããå¿ èŠããããŸãã ãªãªãŒã¹ãã§ã³ã¹ã¯å®å šãªããªã¢ã®ç¹æ®ãªã±ãŒã¹ã§ããããããã®å®è£ ã¯æ£ãããåé·ã§ãã ããã«ãªãªãŒã¹ãã§ã³ã¹ãã€ã³ã¹ããŒã«ãããŠããå Žåãããã»ããµ/ã³ã³ãã€ã©ã¯æé©åã®æ©äŒãå¢ããŸãã .NETéçºããŒã ãå®å šãªéå£ãä»ããŠãããã®æ©èœãå®è£ ããçç±ã説æããã®ã¯å°é£ã§ãã ãã ãããããã¯çŸåšã®å®è£ ã®è©³çްã«ããããå°æ¥å€æŽãããªãããšãä¿èšŒãããã®ã§ã¯ãªãããšãèŠããŠããããšãéèŠã§ãã
ã³ã³ãã€ã©ãŒãšããã»ããµãŒã®æé©å
å床泚æãããã®ã¯ãvolatileããŒã¯ãŒããšãã¡ã¢ãªããªã¢ãèšå®ããããã«èæ ®ããã3ã€ã®é¢æ°ãã¹ãŠããããã»ããµã®æé©åãšã³ã³ãã€ã©ã®æé©åã®äž¡æ¹ã«åœ±é¿ããããšã§ãã
ã€ãŸããããšãã°ããã®ã³ãŒãã¯æåã®äŸã§ç€ºããåé¡ã®å®å šã«æ£ãã解決çã§ãã
public void Bar() { _a = 1; while (_a == 1) { Thread.MemoryBarrier(); } }
æ®çºæ§ã®å±éºæ§
VolatileWriteã¡ãœãããšVolatileReadã¡ãœããã®å®è£ ãèŠããšããã®ãããªåœä»€ã®ãã¢ãåé 眮ã§ããããšãæããã«ãªããŸãã
Thread.VolatileWrite(b) Thread.VolatileRead(a)
ãã®åäœã¯volatileèªã¿åãããã³æžã蟌ã¿ãšããçšèªã®å®çŸ©ã«çµã¿èŸŒãŸããŠãããããããã¯ãã°ã§ã¯ãªããvolatileããŒã¯ãŒãã§ããŒã¯ããã倿°ã䜿çšããæäœãåæ§ã®åäœãããŸãã
ããããå®éã«ã¯ããã®åäœã¯äºæããªããã®ã§ãã
äŸãèããŠã¿ãŸãããïŒ
class Program { volatile int _firstBool; volatile int _secondBool; volatile string _firstString; volatile string _secondString; int _okCount; int _failCount; static void Main(string[] args) { new Program().Go(); } private void Go() { while (true) { Parallel.Invoke(DoThreadA, DoThreadB); if (_firstString == null && _secondString == null) { _failCount++; } else { _okCount++; } Console.WriteLine("ok - {0}, fail - {1}, fail percent - {2}", _okCount, _failCount, GetFailPercent()); Clear(); } } private float GetFailPercent() { return (float)_failCount / (_okCount + _failCount) * 100; } private void Clear() { _firstBool = 0; _secondBool = 0; _firstString = null; _secondString = null; } private void DoThreadA() { _firstBool = 1; //Thread.MemoryBarrier(); if (_secondBool == 1) { _firstString = "a"; } } private void DoThreadB() { _secondBool = 1; //Thread.MemoryBarrier(); if (_firstBool == 1) { _secondString = "a"; } } }
ããã°ã©ã åœä»€ãå®çŸ©ãããæ£ç¢ºãªé åºã§å®è¡ãããå Žåãå°ãªããšã1è¡ã¯åžžã«ãaãã«çãããªããŸãã å®éãåœä»€ã®åé 眮ã«ããããããåžžã«åœãŠã¯ãŸãããã§ã¯ãããŸããã volatileããŒã¯ãŒããé©åãªã¡ãœããã«çœ®ãæããŠããäºæ³ã©ãããçµæã¯å€ãããŸããã
ãã®ããã°ã©ã ã®åäœãä¿®æ£ããã«ã¯ãã¡ã¢ãªããªã¢ããã£ã±ãã®è¡ã®ã³ã¡ã³ããå€ãã ãã§ååã§ãã
ããã©ãŒãã³ã¹Thread.Volatile *ããã³volatileããŒã¯ãŒã
ã»ãšãã©ã®ãã©ãããã©ãŒã ïŒæ£ç¢ºã«ã¯ãæ»ã«ãããŠããIA64ãé€ãWindowsã§ãµããŒããããŠãããã¹ãŠã®ãã©ãããã©ãŒã ïŒã§ã¯ããã¹ãŠã®æžã蟌ã¿ãšèªã¿åãã¯ããããæ®çºæ§æžã蟌ã¿ãšæ®çºæ§èªã¿åãã§ãã ãããã£ãŠãå®è¡æã«ãvolatileããŒã¯ãŒãã¯ããã©ãŒãã³ã¹ã«åœ±é¿ããŸããã ããã©ããããThread.Volatile *ã¡ãœããã¯ãæåã«MethodImplOptions.NoInliningãšããŠããŒã¯ãããã¡ãœããåŒã³åºãèªäœã®ãªãŒããŒããããè² æ ãã2çªç®ã«ãçŸåšã®å®è£ ã§ã¯å®å šãªã¡ã¢ãªããªã¢ãäœæããŸãã ã€ãŸããããã©ãŒãã³ã¹ã®èгç¹ãããã»ãšãã©ã®å ŽåãããŒã¯ãŒãã䜿çšããããšããå§ãããŸãã
åç §è³æ
1 514ããŒãžJoe Duffyãåç §ã Windowsã§ã®äžŠè¡ããã°ã©ãã³ã°
2 誀ã£ãŠå®è£ ãããVolatileWriteãåç §
䜿çšãããæç®ïŒ
- ãžã§ã»ãã»ã¢ã«ãããªã CïŒã®ã¹ã¬ããå
- ãã³ã¹ã»ã¢ãªãœã³ ãã«ãã¹ã¬ããã¢ããªã§ã®äœããã¯æè¡ã®åœ±é¿ãçè§£ãã
- ããã©ã ã»ã¬ã¶ãšã€ã CLR 2.0ã¡ã¢ãªã¢ãã«
- MS ConnectïŒ VolatileWriteãæ£ããå®è£ ãããŠããŸãã
- ECMA-335å ±éèšèªã€ã³ãã©ã¹ãã©ã¯ãã£ïŒCLIïŒ
- CïŒèšèªä»æ§
- ãžã§ããªãŒã»ãªãã¿ãŒã CïŒThird Editionãä»ããCLR
- ãžã§ãŒã»ããã£ãŒã Windowsã§ã®äžŠè¡ããã°ã©ãã³ã°
- ãžã§ã»ãã»ã¢ã«ãããªã CïŒ4.0ã®æŠèŠ