.NETäžã®ãã©ã³ã¹ããŒãç£èŠã·ã¹ãã ã®è² è·ã®é«ããµãŒããŒãèšèšãããšãã«ãåãåé¡ãç®ã®åã§çºçããŸããã ãã®çµæããã£ãã·ã¥ãå¿ èŠã§ããããšã決å®ãããŸããã èŸæžãã£ãã·ã¥ã¯ãConcurrentDictionaryã®ã©ãããŒã«æ ŒçŽãããããã«ãªããŸããã ãã®ãªãã·ã§ã³ã¯ãã¹ã¬ããã»ãŒããªãã£ã¯ã·ã§ããªçšã®æšæºã®.NETããŒã«ã§ãããããããŸã調æ»ããããšãªãæ¡çšãããŸããã ä»ããããã®ãœãªã¥ãŒã·ã§ã³ã®ããã©ãŒãã³ã¹ããã¹ããããšãã§ãã ããã«ã€ããŠã¯ãå®éã«ã¯èšäºã§ãã ãŸãããã®èšäºã®æåŸã«ã¯ãConcurrentDictionaryå ã§ã®é 眮æ¹æ³ã«é¢ããå°ãã®ç 究ããããŸãã
åé¡ã®å£°æ
ããŒãšå€ã®ãã¢ã®ã¹ã¬ããã»ãŒããªã³ã¬ã¯ã·ã§ã³ãå¿ èŠã§ããããã«ããã次ã®ããšãå¯èœã«ãªããŸãã
- èå¥åããŒãæã€ãªããžã§ã¯ããèŠæ±ãã-ãã¡ãããæãé »ç¹ã«äœ¿çšãããŸãã
- æ°ããèŠçŽ ãå€æŽãåé€ãè¿œå ããããšã¯ãŸãã§ãããã¹ã¬ããã®å®å šæ§ã確ä¿ããå¿ èŠããããŸãã
- å€ã®æäœ-ãã¹ãŠã®å€ãèŠæ±ããå€ãªããžã§ã¯ãã®ããããã£ã§æ€çŽ¢ããŸãã
æ¡é 3ã¯èŸæžã³ã¬ã¯ã·ã§ã³ã§ã¯äžè¬çã§ã¯ãªãããããã®æ§æã®äž»ãªãã¬ãŒããšãªããŸãã ãã ããã«ãã¯ã¢ããããŒãã«ã®ãã£ãã·ã¥ã䜿çšããå Žåããã®ãããªç¶æ³ã¯é¿ããããŸããïŒããšãã°ãç·šéã®ããã«ç®¡çããã«ã«ãã£ã¯ã·ã§ããªå šäœã衚瀺ããã®ã¯ç°¡åã§ãïŒã
ãã£ãã·ã¥ã䜿çšãããããŸããŸãªã·ã¹ãã ãæ€èšããŠã¿ãŸãããã ãããã¯ãèŸæžã䜿çšããæäœã®é »åºŠãç°ãªããŸãã ãããã®æäœã®ã¿ã€ãã圢åŒåããŸãã
- GET-ããŒã«ãããªããžã§ã¯ãã®ãªã¯ãšã¹ãã
- ADD-æ°ããããŒã§æ°ãããªããžã§ã¯ããè¿œå ïŒãã®ãããªããŒãæ¢ã«ååšããå Žåã¯äžæžãïŒ
- UPDATE-æ¢åã®ããŒã®æ°ããå€ïŒããŒããªãå Žåã¯äœãããŸããïŒ
- SEARCH-ã€ãã¬ãŒã¿ãŒã䜿çšããŸãããã®å Žåãæåã®é©åãªå€ãæ€çŽ¢ããŸã
ãã¹ãåå è ã®ãªã¹ã
- ConcurrentDictionary ã ããã¯ãã¹ã¬ããã»ãŒããçµ±åãããMicrosoftã®ã¿ãŒã³ããŒãœãªã¥ãŒã·ã§ã³ã§ãã 䟿å©ãªã¡ãœããTryGetValueãTryAddãTryUpdateãAddOrUpdateãTryDeleteãå®è£ ããŸããããã«ããã競å解決ããªã·ãŒãç°¡åã«èšå®ã§ããŸãã å®è£ æ©èœã«ã€ããŠã¯ãèšäºã®æåŸã§èª¬æããŸãã
- Monitorãä»ããããã¯ä»ãã®èŸæž ã æãæ£é¢ããã®è§£æ±ºçã¯ããã¹ãŠã®éã¹ã¬ããã»ãŒãæäœãããã¯æ§é ã«ã©ãããããããšã§ãã
- ReaderWriterLockãä»ããããã¯ä»ãã®èŸæž ã 以åã®ãœãªã¥ãŒã·ã§ã³ã®æé©å-æäœã¯èªã¿åãæäœãšæžã蟌ã¿æäœã«åããããŸãã ãããã£ãŠãè€æ°ã®ã¹ããªãŒã ãåæã«èªã¿åãããšãã§ããèšé²ã«ã¯æä»çã¢ã¯ã»ã¹ãå¿ èŠã§ãã
- ReaderWriterLockSlimãä»ããããã¯ä»ãã®èŸæž ã åºæ¬çã«åãã§ãããæ°ããã¯ã©ã¹ã䜿çšããŸãïŒååž°å¶åŸ¡èšå®ãè¿œå ïŒã ãã®ã¿ã¹ã¯ã®ã³ã³ããã¹ãã§ã¯ãReaderWriterLock以å€ã衚瀺ããå¿ èŠã¯ã»ãšãã©ãããŸããã
- Wintellect PowerThreadingã©ã€ãã©ãªã®OneManyResourceLockerãä»ããããã¯ä»ãèŸæž -Jeffrey Richterã«ããReaderWriterLockã®ããªãããŒãªå®è£ ã NuGetããã±ãŒãžã§ã¯ãªããå ¬åŒãµã€ãã®ããŒãžã§ã³ã䜿çšãããããšãæ確ã«ããŸããããŒãžã§ã³ãç°ãªããããæ°ã«å ¥ããªãã£ãããã§ãã
- Hashtable.Synchronized ã ãŸããMicrosoftã®æ¢æã®ãœãªã¥ãŒã·ã§ã³-ã¹ã¬ããã»ãŒããªã€ã³ãã¯ãµãŒãæäŸããŸãã éãžã§ããªãã¯ã³ã¬ã¯ã·ã§ã³ïŒããã¯ã¹åãèªã¿ãããã®äœäžïŒã§ãããTryãã¬ãã£ãã¯ã¹ãæã€ã¡ãœããããªãããã䜿çšããã®ã¯äžäŸ¿ã§ãïŒåæã«è¿œå /æŽæ°ããããã®ããªã·ãŒãèšå®ããããšã¯äžå¯èœã§ãïŒã
ãã³ãã©ãŒã®å®è£ æ¹æ³ãæ£ç¢ºã«ç°¡æœã«èª¬æããŸãã
- GETæäœïŒãã¹ãŠã®åå è ã¯ãIDictionaryã€ã³ã¿ãŒãã§ã€ã¹ããã¹ã¬ããã»ãŒããªTryGetValueã¡ãœããã䜿çšããŸãã Hashtableã®å Žåãã¿ã€ãã³ãŒãåãããã€ã³ãã¯ãµãŒã䜿çšãããŸãã
- ADDæäœïŒConcurrentDictionaryã®å Žå-AddOrUpdateãDictionaryã®å Žå-ããã¯ã®æžã蟌ã¿ãšã€ã³ãã¯ãµãŒã®è¿œå ãHashtableã®å Žå-ããã¯ãªãã®ã€ã³ãã¯ãµãŒã®è¿œå ã
- UPDATEæäœïŒConcurrentDictionaryã®å Žåãæåã«TryGetValueã次ã«TryUpdateã
ãã®ã¡ãœããã¯ãããã2ã€ã®ã¡ãœããéã§äžŠè¡æŽæ°ãå®è¡ã§ãããšããç¹ã§èå³æ·±ãïŒãã¹ãäžã«ãæããã«ãªã£ãïŒã ãã®å ŽåãoldValueãTryUpdateã«æž¡ãããããããã®ãŸããªã±ãŒã¹ã§ã¯æžãæãã倱æããŸãã ãã£ã¯ã·ã§ããªã®å ŽåãContainsKeyãä»ããŠå¯çšæ§ã確èªããæåããå Žåã¯æžã蟌ã¿ããã¯ãèšå®ããŠå€ãäžæžãããŸãã Hashtableçšã®äŸ¿å©ãªTryUpdateããªããããããŒã®ååšã確èªããæéããããããè¿œå ã®å Žåã®ããã«ãå€ã¯ã€ã³ãã¯ãµãŒã«ãã£ãŠäžæžããããŸãïŒãã®ã³ã¬ã¯ã·ã§ã³ã§ã¯ãããã¯éèŠã§ã¯ãããŸãã-ãšã«ããããªãæªãã£ãã§ãïŒã - æäœSEARCH ïŒConcurrentDictionaryã®å ŽåãLINQã®FirstOrDefaultã䜿çšãããæ®ãã®å ŽåãåãFirstOrDefaultã«å¯ŸããŠèªã¿åãããã¯ã䜿çšãããŸãã
è©Šéšå°
ãã¹ãçšã«ãã³ã³ãœãŒã«ã¢ããªã±ãŒã·ã§ã³ãäœæãããŸããïŒ ãªã³ã¯ ïŒã
- ç¹å®ã®ãã¹ãŠã®ã¿ã€ãã®æäœãåŠçã§ããäžé£ã®ãã³ãã©ãŒãäœæãããŸãã
- Nåã®èŠçŽ ã®ãã£ã¯ã·ã§ããªãäœæãããŸãïŒããã©ã«ãã§ã¯10,000ïŒã
- Mã®éã®ããŸããŸãªã¿ã€ãã®ã¿ã¹ã¯ã®ã³ã¬ã¯ã·ã§ã³ãäœæãããŸãïŒããã©ã«ãã§ã¯10,000ïŒã
- åãã³ãã©ãŒã¯ãçæãããèŸæžå ã®ãã¹ãŠã®ã¿ã¹ã¯ïŒãã¹ãŠã®ãã³ãã©ãŒã«å ±éïŒã®äžŠååŠçãå®è¡ããŸãã
- å®éšïŒãã€ã³ã2ã4ïŒã¯ãæå®ãããåæ°ïŒããã©ã«ãã§ã¯10åïŒå®è¡ãããååŸãããæéã¯å¹³åãããŸãã 枬å®ã¯ãCore 2 Quad 2.66 GHzãš8 GBã®ã¡ã¢ãªãæèŒãããã·ã³ã§è¡ãããŸããã
ããã©ã«ãå€ã¯éåžžã«å°ããã§ãããå€ãå¢ãããŠãåºæ¬çã«ã¯äœãå€ãããŸããã
è©Šéšçµæ
ãã¹ãã¯ãæäœã®ã¿ã€ãããšã«ç°ãªãåæ£ãªãã·ã§ã³ã䜿çšããŠå®è¡ãããããŒãã«ã倧ããããããšãå€æããŸãããå šäœãããã§ç¢ºèªã§ããŸãïŒ ãªã³ã¯ ïŒ ããããããããããã«ãåèšæäœæ°ã®å€ã«ããèªã¿åãã®å²åã«å¿ããŠãã€ã¯ãç§åäœã§ãã¹ãå®è¡æéã®ã°ã©ãã瀺ããŸãïŒæžã蟌ã¿æäœã¯20ïŒ ã«åºå®ãããæ®ãã¯ããŒã«ãã£ãŠèªã¿åãããŸãïŒã
çµè«
ãã¹ãŠã®åå è ã®ããã©ãŒãã³ã¹ã¯ãæžã蟌ã¿æäœã®åæ°ã«é¢ä¿ãªããå€ããšã®èªã¿åãåæ°ã«æ¯äŸããŠäœäžããŸãã
- ConcurrentDictionary ã å®éšã«ããããã®ããŒã«ã¯ãã®ã¿ã¹ã¯ã«æé©ã§ããããšã瀺ãããŠããŸãã å€ã«ããèªã¿åãã¯ããã©ãŒãã³ã¹ã«å€§ãã圱é¿ããŸãããä»ã®åå è ãããé«éã§ãã
- èŸæž+ã¢ãã¿ãŒ ã å€§å¹ ã«é ããæåŸ ãããçµæã
- èŸæž+ ReaderWriterLock ã 以åã®ããŒãžã§ã³ã®æé©åããã¹ãŠæåŸ
ãããŠããŸãã
èšé²æäœãå€ãã»ã©ãå·®ã¯å°ãããªãããšã«æ³šæããŠãã ããã ããæç¹ããããããã¯ããã»ã¹èªäœã®ãªãŒããŒããããå°ãããªããããMonitorãããã«å¥œãŸããç¶æ ã«ãªããŸãã - èŸæž+ ReaderWriterLockSlim äœããã®çç±ã§ãç§ã¯åçŽãªã¢ãã¿ãŒã§ãããäœãšã倱ããŸããã 匷åãããæ©èœïŒä»¥åã®ããŒãžã§ã³ãšæ¯èŒããŠïŒãããã©ãŒãã³ã¹ã«åœ±é¿ããããããã調çããæ¹æ³ãããããŸããã
- èŸæž+ OneManyResourceLock ã ãªãã¿ãŒã¯ãèªã¿åã/æžã蟌ã¿ããã¯ãããã¹ãŠãçµãåºããããã§ãã ãã¹ãçµæã«ãããšãããã¯èŸæžã®æé䜿çšã§ãã ããããConcurrentDictionaryã¯ããã«é«éã§ãã
- ããã·ã¥ããŒãã« ã äºæ³ããã倱æã ããããç§ã¯ããã誀ã£ãŠäœ¿çšããŸããããä»ã®åå è ã«å¹æµããçµæãåŸãããšãã§ãããšã¯æããŸããã ãšã«ãããäžè¬çã§ãªãã³ã¬ã¯ã·ã§ã³ãæ±ãã®ã¯ã©ããããããæ°ã®ãããããã§ããã
å éšããã€ã¹ConcurrentDictionary
åè ã詳ããèŠãŠã¿ãŸããããã€ãŸããConcurrentDictionaryã®ãœãŒã¹ãèŠãŠã¿ãŸãããã
ãã®ã¯ã©ã¹ãäœæãããšã Capacityããã³ConcurrencyLevelã® 2ã€ã®ãã©ã¡ãŒã¿ãŒãèšå®ãããŸãã æåã®ïŒ Capacity ïŒã¯ã³ã¬ã¯ã·ã§ã³ã«éŠŽæã¿ããããå éšã³ã¬ã¯ã·ã§ã³ãå±éããã«èšé²ã§ããèŠçŽ ã®æ°ãèšå®ããŸãã ãã®å Žåããªã³ã¯ããããªã¹ããäœæãããŸãïŒm_bucketsããã¹ã±ãããšåŒã°ããŸãïŒ ããã©ã«ãå€ã¯31ã§ãã
2çªç®ã®ãã©ã¡ãŒã¿ãŒïŒ ConsurrencyLevel ïŒã¯ãèŸæžã«åæã«æžã蟌ãããšãã§ããã¹ã¬ããã®æ°ã決å®ããŸãã ããã¯ãã¢ãã¿ãŒã«ããããã¯çšã®ãªããžã§ã¯ãã®ã³ã¬ã¯ã·ã§ã³ãäœæããããšã«ããå®çŸãããŸãã ãã®ãããªåããããã³ã°ãªããžã§ã¯ãã¯ãã»ãŒåãæ°ã®ãã¹ã±ãããæ åœããŸãã ããã©ã«ãå€ã¯Environment.ProcessorCount * 4ã§ãã
ãããã£ãŠããã£ã¯ã·ã§ããªå ã®åãªããžã§ã¯ãã¯ããããååšãããã¹ã±ãããšæžã蟌ã¿çšã®ããã¯ãªããžã§ã¯ãã«äžæã«é¢é£ä»ããããŸãã ããã¯ã次ã®æ¹æ³ã§è¡ãããŸãã
/// <summary> /// Computes the bucket and lock number for a particular key. /// </summary> private void GetBucketAndLockNo( int hashcode, out int bucketNo, out int lockNo, int bucketCount, int lockCount) { bucketNo = (hashcode & 0x7fffffff) % bucketCount; lockNo = bucketNo % lockCount; Assert(bucketNo >= 0 && bucketNo < bucketCount); Assert(lockNo >= 0 && lockNo < lockCount); }
å¥åŠãªããšã«ãConcurrencyLevel = 1ã§ãã£ãŠããConcurrentDictionaryã¯éåžžã®ããã¯èŸæžãããé«éã§ãã ãŸããã¯ã©ã¹ãã€ãã¬ãŒã¿ãä»ããŠäœ¿çšããããã«æé©åãããŠããããšã泚ç®ã«å€ããŸãïŒãã¹ãã瀺ãããšããïŒã ç¹ã«ãToArrayïŒïŒã¡ãœãããåŒã³åºããšããã¹ãŠã®ãã¹ã±ããã§ããã¯ãå®è¡ãããã€ãã¬ãŒã¿ãŒãæ¯èŒçå®äŸ¡ã«äœ¿çšãããŸãã
äŸïŒdictionary.Values.ToArrayïŒïŒãããdictionary.SelectïŒx => x.ValueïŒ.ToArrayïŒïŒã䜿çšããããšããå§ãããŸãã
ãã®èšäºã¯ãå瀟ã®äžæµéçºè ã«ãã£ãŠæžãããŸããã
ãæž èŽããããšãããããŸããïŒ