ãã®èšäºã§ã¯ãã1åéãã®ãäœæ¥çšã«ãªããžã§ã¯ããäœæãããããšãå€ããã«ãã¹ã¬ããïŒããã³ããã ãã§ã¯ãªãïŒCïŒã¢ããªã±ãŒã·ã§ã³ã®ããã©ãŒãã³ã¹ãæ¹åããæ¹æ³ã説æããŸãã
ãã«ãã¹ã¬ããããã³ããããã³ã°åæãVS2012ã®çµã¿èŸŒã¿ãããã¡ã€ã©ãŒã®äœ¿çšãããã³å°ããªãã³ãããŒã¯ã«ã€ããŠå°ã説æããŸãã
ã¯ããã«
ãªããžã§ã¯ãããŒã«ã¯ãçæçãªãã¶ã€ã³ãã¿ãŒã³ã§ãããåæåãããŠããã«äœ¿çšã§ãããªããžã§ã¯ãã®ã»ããã§ãã
ãªãå¿ èŠãªã®ã§ããïŒ ç°¡åã«èšãã°ãæ°ãããªããžã§ã¯ããåæåããéã®ããã©ãŒãã³ã¹ãåäžãããããšã¯å€§ããªè²»çšã§ãã ãã ãã.NETã®çµã¿èŸŒã¿ã®ã¬ããŒãžã³ã¬ã¯ã¿ãŒã¯ãçåœã®ãªããžã§ã¯ãã®ç Žå£ã«å¯ŸåŠãããããããŒã«ã®é©çšå¯èœæ§ã¯æ¬¡ã®åºæºã«ãã£ãŠå¶éãããããšãç解ããããšãéèŠã§ãã
- äœæããã³/ãŸãã¯ç Žå£ããã®ã«é«äŸ¡ãªãªããžã§ã¯ãïŒäŸïŒãœã±ããããããŒã管çãããŠããªããªãœãŒã¹ïŒ;
- åå©çšã®ããã«ãªããžã§ã¯ããã¯ãªãŒãã³ã°ããæ¹ããæ°ãããªããžã§ã¯ããäœæãããããå®äŸ¡ã§ãïŒãŸãã¯ç¡æã§ãïŒã
- éåžžã«å€§ããªãªããžã§ã¯ãã
æåŸã®ãã€ã³ãã«ã€ããŠå°ã説æããŸãã ãªããžã§ã¯ããã¡ã¢ãªã§85,000ãã€ã以äžãå æããŠããå Žåã第2äžä»£ã®ã¬ããŒãžã³ã¬ã¯ã·ã§ã³ã§å€§ããªãªããžã§ã¯ãããŒãã«åé¡ãããèªåçã«ãé·åœããªããžã§ã¯ãã«ãªããŸãã ãã®æçåã«è¿œå ãïŒãã®ããŒãã¯çž®å°ããŸããïŒãã¡ã¢ãªã®äžè¶³ãšããæœåšçãªåé¡ãåŸãããã«ãäžå®ã®å²ãåœãŠ/ç Žå£ãè¡ããŸãã
ããŒã«ã®ã¢ã€ãã¢ã¯ã次ã®ã·ããªãªã䜿çšããŠãé«äŸ¡ãªããªããžã§ã¯ãã®åå©çšãæŽçããããšã§ãã
var obj = pool.Take(); // . obj.DoSomething(); pool.Release(obj); // ("") ,
ãã®ã¢ãããŒãã®åé¡ïŒ
- ãªããžã§ã¯ãã®æäœåŸã以åã®äœ¿çšãåŸç¶ã®ãªããžã§ã¯ãã«åœ±é¿ãäžããªãããã«ããªããžã§ã¯ããåæç¶æ ã«ãªã»ããããå¿ èŠãããå ŽåããããŸãã
- ããŒã«ã¯ãéåžžããã«ãã¹ã¬ããã·ã¹ãã ã§äœ¿çšããããããã¹ã¬ããã»ãŒããæäŸããå¿ èŠããããŸãã
- ããŒã«ã¯ãçºè¡å¯èœãªãªããžã§ã¯ãããªãå Žåã«åŠçããå¿ èŠããããŸãã
ãããã®åé¡ã念é ã«çœ®ããŠãæ°ããã¯ã©ã¹ã®èŠä»¶ãã³ã³ãã€ã«ãããŸããã
- ã³ã³ãã€ã«æ®µéã§ã®ããŒã«ã®ã¿ã€ãã»ãŒããã£ã
- ããŒã«ã¯ããµãŒãããŒãã£ãå«ããã¹ãŠã®ã¯ã©ã¹ã§æ©èœããŸãã
- ã³ãŒãã§ã®ç°¡åãªäœ¿çšã
- äžè¶³ãããå Žåã®æ°ãããªããžã§ã¯ãã®èªåå²ãåœãŠããŠãŒã¶ãŒåæåã
- éžæãããªããžã§ã¯ãã®ç·æ°ãå¶éããŸãã
- ãªããžã§ã¯ããããŒã«ã«æ»ã£ããšãã«ãªããžã§ã¯ããèªåã¯ãªãŒãã³ã°ããŸãã
- ã¹ã¬ããã»ãŒããã£ïŒã§ããã°ãæå°éã®åæã³ã¹ãïŒã
- è€æ°ã®ããŒã«ã€ã³ã¹ã¿ã³ã¹ã®ãµããŒãïŒãããããå°ãªããšãæãåçŽãªå¶åŸ¡ã¯ããªããžã§ã¯ããããŒã«ã«æ»ãããã«ããããšã§ãïŒã
䜿çšäžã®åé¡ã解決ãã
äžéšã®å®è£ ã§ã¯ããªããžã§ã¯ãããŒã«ããµããŒãããããã«ããªããžã§ã¯ãã¯IPoolableã€ã³ã¿ãŒãã§ã€ã¹ãªã©ãå®è£ ããå¿ èŠããããŸãããç§ã®ã¿ã¹ã¯ã¯ãç¶æ¿ã®ããã«éããããŠããå Žåã§ããããŒã«ããã¹ãŠã®ã¯ã©ã¹ã§åäœããããã«ããããšã§ããã ãããè¡ãããã«ãPoolSlotæ±çšã·ã§ã«ãäœæãããŸããããã®ã·ã§ã«ã«ã¯ããªããžã§ã¯ãèªäœãšããŒã«ãžã®ãªã³ã¯ãå«ãŸããŠããŸãã ããŒã«èªäœã¯ããããã®ã¹ããããæ ŒçŽããããã®æœè±¡çãªæ±çšã¯ã©ã¹ã§ãããæ°ãããªããžã§ã¯ããäœæããŠå€ããªããžã§ã¯ããã¯ãªãŒãã³ã°ããããã®2ã€ã®æªå®çŸã¡ãœããããããŸãã
public abstract class Pool<T> { public PoolSlot<T> TakeSlot() {...} // " " public void Release(PoolSlot<T> slot) {...} // " " /* ... */ // : protected abstract T ObjectConstructor(); // , protected virtual void CleanUp(T item) {} // , }
SocketAsyncEventArgsã¯ã©ã¹ã®äœ¿çšäŸ
ããŒã«å®çŸ©
public class SocketClientPool : Pool<SocketAsyncEventArgs> { private readonly int _bufferSize; public SocketClientPool(int bufferSize, int initialCount, int maxCapacity) : base(maxCapacity) { if (initialCount > maxCapacity) throw new IndexOutOfRangeException(); _bufferSize = bufferSize; TryAllocatePush(initialCount); // protected-; } protected override SocketAsyncEventArgs ObjectConstructor() { var args = new SocketAsyncEventArgs(); args.SetBuffer(new byte[_bufferSize], 0, _bufferSize); return args; } protected override void CleanUp(SocketAsyncEventArgs @object) { Array.Clear(@object.Buffer, 0, _bufferSize); } }
ã³ãŒãã§äœ¿çšïŒ
var pool = new SocketClientPool(1024, 5, 10); // , /* ...- ... */ var slot = pool.TakeSlot(); // var args = slot.Object; // - pool.Release(slot); //
ãŸãã¯ãã®ããã«ïŒ
using(var slot = pool.TakeSlot()) // PoolSlot IDisposable { var args = slot.Object; }
Socket.XxxAsyncã¡ãœããã¯PoolSlot <SocketAsyncEventArgs>ã§ã¯ãªãSocketAsyncEventArgsãæ£ç¢ºã«åãå ¥ãããããéåæ.NETã¢ãã«ããã³/ãŸãã¯åãSocketã¯ã©ã¹ã®éåæã¡ãœããã«ç²ŸéããŠãã人ã¯ããã®ãããªå®è£ ã®äœ¿çšãé£ããããšãç¥ã£ãŠããŸãã ã ã¡ãœãããåŒã³åºãããã«ããã¯éèŠã§ã¯ãããŸããããçµäºãã³ãã©ãŒã®ã©ãã§ã¹ããããååŸã§ããŸããïŒ
1ã€ã®ãªãã·ã§ã³ã¯ããªããžã§ã¯ãã®äœææã«SocketAsyncEventArgs.UserTokenããããã£ã«ã¹ããããä¿åããããšã§ããããã«ã¯ãããŒã«ã«HoldSlotInObjectããªãŒããŒã©ã€ãããã¡ãœããããããŸãã
äŸã®ãªãŒããŒã©ã€ã
protected override void HoldSlotInObject(SocketAsyncEventArgs @object, PoolSlot<SocketAsyncEventArgs> slot) { @object.UserToken = slot; } /* ...- ... */ pool.Release(args.UserToken as PoolSlot<SocketAsyncEventArgs>);
ãã¡ããããã¹ãŠã®ãªããžã§ã¯ãããŠãŒã¶ãŒã«ãã®ãããªããããã£ãæäŸããããã§ã¯ãããŸããã ã¯ã©ã¹ããŸã ç¶æ¿ããéããããŠããªãå Žåãã¹ããããæ ŒçŽããããã®1ã€ã®ããããã£ãæã€ç¹å¥ãªIPoolSlotHolderã€ã³ã¿ãŒãã§ã€ã¹ãæäŸãããŸãã ãªããžã§ã¯ãã«ã¹ããããå«ãŸããããšãä¿èšŒãããŠããããšãããã£ãŠããå Žåã¯ãããŒã«èªäœã®åå«ã§è¡ããããªããžã§ã¯ãèªäœãè¿ã/åãåãïŒããã³ã¹ããããååŸããïŒTakeObject / Releaseã¡ãœãããè¿œå ããã®ãè«ççã§ãã
æ¹åãããããŒã«ã®å®è£
ã®ç°¡çŽ åïŒIPoolSlotHolderãå®è£
ãããªããžã§ã¯ãã®å ŽåïŒ
public abstract class PoolEx<T> : Pool<T> where T : IPoolSlotHolder { public T TakeObject() { ... } public void Release(T @object) { ... } protected sealed void HoldSlotInObject(T @object, PoolSlot<T> slot) { ... } // }
次ã«ãå éšã®ããããã³ãã®éçºã«ç²Ÿéããããšãææ¡ããŸãã
ä¿ç®¡
ConcurrentStackã³ã¬ã¯ã·ã§ã³ã¯ããããŒã«ã«ããªããžã§ã¯ããæ ŒçŽããããã«äœ¿çšãããŸãã ããŒã«ã®ããã€ãã®ã€ã³ã¹ã¿ã³ã¹ã䜿çšããã«ã¯ããã®ããŒã«ã«ãã£ãŠäœæããããªããžã§ã¯ãã®èšé²ãä¿æããå¿ èŠããããŸããã
ãããã£ãŠãConcurrentDictionaryã«åºã¥ããŠãã¬ãžã¹ããªããå°å ¥ãããŸãããConcurrentDictionaryã«ã¯ãããŒã«ãšãªããžã§ã¯ãå¯çšæ§ãã©ã°ã«ãã£ãŠäœæãããã¹ãããã®IDãå«ãŸããŸãïŒtrue-ãããŒã«å ããfalse-ãããŒã«å ã«ãªããïŒã
ããã«ããã1ã€ã®ç³ã§2矜ã®é³¥ãäžåºŠã«æ®ºãããšãã§ããŸããïŒåããªããžã§ã¯ãã®èª€ã£ãå€éæ»ããé²ãããïŒã¹ã¿ãã¯ã¯ãããã«æ ŒçŽãããŠãããªããžã§ã¯ãã®äžææ§ãä¿èšŒããªãããïŒãå¥ã®ããŒã«ã§äœæããããªããžã§ã¯ãã®æ»ããé²ããŸãã ãã®ã¢ãããŒãã¯äžæçãªè§£æ±ºçã§ãããããããåãé€ããŸããã
ãã«ãã¹ã¬ãã
ããŒã«ã®å€å žçãªå®è£ ã§ã¯ãã»ããã©ïŒ.NETã§ã¯Semaphoreããã³SemaphoreSlimïŒã䜿çšããŠããªããžã§ã¯ãã®æ°ãŸãã¯ä»ã®åæããªããã£ããã«ãŠã³ã¿ãŒãšçµã¿åãããŠè¿œè·¡ããŸãããConcurrentDictionaryã®ãããªConcurrentStackã¯ã¹ã¬ããã»ãŒããªã³ã¬ã¯ã·ã§ã³ãªã®ã§ããªããžã§ã¯ãã®å ¥å/åºåã調æŽããå¿ èŠã¯ãããŸããã ConcurrentStack.Countããããã£ãåŒã³åºããšããã¹ãŠã®èŠçŽ ã®å®å šãªåæãçºçããããšã«æ³šæããŠãã ãããããã«ã¯ããªãã®æéãããããããç¬èªã®èŠçŽ ã«ãŠã³ã¿ãŒãè¿œå ããããšã«ããŸããã ãã®çµæãããŒã«ã§ã®2ã€ã®ãã¢ãããã¯ãæäœïŒPushããã³TryPopïŒãååŸãããŸããããããã«åºã¥ããŠãä»ã®ãã¹ãŠãæ§ç¯ãããŸããã
ç°¡åãªæäœã®å®è£
private void Push(PoolSlot<T> item) { _registry[token.Id] = true; // : " " _storage.Push(item); // Interlocked.Increment(ref _currentCount); } private bool TryPop(out PoolSlot<T> item) { if (_storage.TryPop(out item)) // { Interlocked.Decrement(ref _currentCount); _registry[token.Id] = false; // : " " return true; } item = default(PoolSlot<T>); return false; }
æ¢åã®ãªããžã§ã¯ãã®å ¥åºåã«å ããŠãæ°ãããªããžã§ã¯ããæå®ãããäžéã«åæããŠå²ãåœãŠãå¿ èŠããããŸãã
ããã§ã¯ãããŒã«å ã®èŠçŽ ã®æ倧æ°ïŒäžéïŒã§åæåãããæ°ãããªããžã§ã¯ããäœæããããã³ã«1ãåŒãã»ããã©ãé©çšããŸãããåé¡ã¯ããŒãã«éãããšãããŒããããã¯ããããšã§ãã ãã®ç¶æ³ããæãåºãæ¹æ³ã¯ãSemaphoreSlim.WaitïŒ0ïŒã¡ãœãããåŒã³åºãããšã§ãããã®ã¡ãœããã¯ãã»ããã©ã®çŸåšã®å€ "0"ã䜿çšããŠãã»ãŒé 延ãªãfalseãè¿ããŸããããã®æ©èœã®è»œéã¢ããã°ãèšè¿°ããããšã«ããŸããã ãããLockFreeSemaphoreã¯ã©ã¹ã®è¡šç€ºæ¹æ³ã§ãããé 延ãªãã§ãŒãã«éãããšfalseãè¿ããŸãã å éšåæã®å Žåãé«éã®CASæäœInterlocked.CompareExchangeã䜿çšããŸãã
ã»ããã©ã§CASæäœã䜿çšããäŸ
public bool TryTake() // true, , false ( ) { int oldValue, newValue; do { oldValue = _currentCount; // newValue = oldValue - 1; // if (newValue < 0) return false; // 0 - false } while (Interlocked.CompareExchange(ref _currentCount, newValue, oldValue) != oldValue); // , return true; }
ãããã£ãŠãããªããžã§ã¯ããååŸãããããŒã«æäœã¯ã次ã®ã¢ã«ãŽãªãºã ã«åŸã£ãŠæ©èœããŸãã
- ã¹ãã¬ãŒãžãããªããžã§ã¯ããååŸããããšããŠããŸãïŒååšããªãå ŽåïŒã
- ã»ããã©ããŒãïŒäžéã«éããïŒã®å Žåãæ°ãããªããžã§ã¯ããäœæããããšããŠããŸã-ãã€ã³ã3ã
- ææªã®ã·ããªãªã¯ããªããžã§ã¯ããåå©ã®çµããã«æ»ãããšã§ãã
æåã®çµæãæé©åãšãªãã¡ã¯ã¿ãªã³ã°
ãªããžã§ã¯ãããŒã«ãæ¬åœã«å¿ èŠã§ããïŒ ããã¯ç¶æ³æ¬¡ç¬¬ã§ãã ãå žåçãªãµãŒããŒãªããžã§ã¯ããã§ããSocketAsyncEventArgsã䜿çšããŠã1024ãã€ãã®ãããã¡ãŒïŒç§åäœã®æéãããŒãªã³ã°ãæå¹ïŒã䜿çšããå°ããªãã¹ãã®çµæã次ã«ç€ºããŸãã
æ°ããããããã£ã®ãªã¯ãšã¹ã | ã·ã³ã°ã«ã¹ã¬ãããããŒã«ãªã | ã·ã³ã°ã«ã¹ã¬ãããããŒã«ä»ã | 25ã¿ã¹ã¯*ãããŒã«ãªã | 25ã¿ã¹ã¯*ãããŒã«ãã |
1,000 | 0.002 | 0.003 | 0.027 | 0.009 |
10,000 | 0.010 | 0.001 | 0.272 | 0.039 |
25,000 | 0.030 | 0.003 | 0.609 | 0.189 |
50,000 | 0.048 | 0.006 | 1.285 | 0.287 |
1,000,000 | 0.959 | 0.125 | 27.965 | 8.345 |
* task-.NET 4.0以éã®TPLã©ã€ãã©ãªã®System.Threading.Tasks.Taskã¯ã©ã¹
ããŒã«ã䜿çšãããã«ãã¹ã¬ãããã¹ãã§ã®ãããã¡ã€ã©ãŒVS2012ã®ééã®çµæïŒ

ã芧ã®ãšããããã¹ãŠã¯ConcurrentStack.TryPopã¡ãœããã«äŸåããŠããŸãããããã¯ïŒæ³å®ããŠããããã«ïŒå éããå ŽæããããŸããã 2çªç®ã¯ããã¬ãžã¹ããªããžã®èšŽãã§ãããäž¡æ¹ã®æ¥åã§çŽ14ïŒ ãéžæããŠããŸãã
ååãšããŠãããŒã«å ã®2çªç®ã®ã³ã¬ã¯ã·ã§ã³ã®ãµããŒãã¯ããšã«ããæŸèæã®ããã«èŠããããããããŒã«å /ããŒã«å ã§ã¯ãªãããšããèšå·ãã¹ãããèªäœã«è»¢éãããã¬ãžã¹ããªã¯å®å šã«åé€ãããŸããã ãªãã¡ã¯ã¿ãªã³ã°åŸã®ãã¹ãçµæïŒäºæ³ã©ããã®æé·ã30ã40ïŒ ïŒïŒ
æ°ããããããã£ã®ãªã¯ãšã¹ã | ããŒã«ä»ãã®25ã®ã¿ã¹ã¯ |
25,000 | 0.098 |
1,000,000 | 5.751 |
ããã§ãããããšãã§ãããšæããŸãã
ãããã«
ã¿ã¹ã¯ãã©ã®ããã«è§£æ±ºãããããç°¡åã«æãåºããŠãã ããã
- ã³ã³ãã€ã«æ®µéã§ã®åå®å šæ§-ãžã§ããªãã¯ã¯ã©ã¹ã®äœ¿çšã
- ã¯ã©ã¹ã®ããããŒã«ã®åäœã¯ãç¶æ¿ãªãã®æ±çšã·ã§ã«ã®äœ¿çšã§ãã
- 䜿ããããã¯ãusingã³ã³ã¹ãã©ã¯ãïŒIDisposableã€ã³ã¿ãŒãã§ã€ã¹ã®ã·ã§ã«ã«ããå®è£ ïŒã§ãã
- æ°ãããªããžã§ã¯ãã®èªåéžæã¯ãæœè±¡çãªPool.ObjectConstructorã¡ãœããã§ãããã®ã¡ãœããã§ã¯ããªããžã§ã¯ãã奜ããªããã«åæåãããŸãã
- ãªããžã§ã¯ãã®æ°ãå¶éããããšã¯ãã»ããã©ã®è»œéããŒãžã§ã³ã§ãã
- ãªããžã§ã¯ããè¿ããããšãã«ãªããžã§ã¯ããèªåã¯ãªãŒãã³ã°ããã®ã¯ãä»®æ³Pool.CleanUpã¡ãœããã§ãããã®ã¡ãœããã¯ãããŒã«ãè¿ããããšãã«èªåçã«åŒã³åºãããŸãã
- ã¹ã¬ããã»ãŒããã£-ConcurrentStackããã³CASæäœïŒInterlockedã¯ã©ã¹ã®ã¡ãœããïŒã®ã³ã¬ã¯ã·ã§ã³ã䜿çšããŸãã
- è€æ°ã®ããŒã«ã€ã³ã¹ã¿ã³ã¹ã®ãµããŒãâ Poolã¯ã©ã¹ã¯éçã§ã¯ãªããã·ã³ã°ã«ãã³ã§ã¯ãªããæäœã®æå¹æ§ãã§ãã¯ãæäŸããŸãã
åäœãã¹ããšãã¹ãã¢ããªã±ãŒã·ã§ã³ãå«ããœãŒã¹ã³ãŒãïŒ Github
èå³ãããã°ããã®ããŒã«ãæžãããã°ããã®éåæTCPããã³UDPãœã±ãããµãŒããŒãå®è£ ããããšã§ãèšäºãç¶ããããšãã§ããŸãã