ConcurrentBag.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. //
  2. // ConcurrentBag.cs
  3. //
  4. // Author:
  5. // Jérémie "Garuma" Laval <[email protected]>
  6. //
  7. // Copyright (c) 2009 Jérémie "Garuma" Laval
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining a copy
  10. // of this software and associated documentation files (the "Software"), to deal
  11. // in the Software without restriction, including without limitation the rights
  12. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. // copies of the Software, and to permit persons to whom the Software is
  14. // furnished to do so, subject to the following conditions:
  15. //
  16. // The above copyright notice and this permission notice shall be included in
  17. // all copies or substantial portions of the Software.
  18. //
  19. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. // THE SOFTWARE.
  26. #if NET_4_0
  27. using System;
  28. using System.Collections;
  29. using System.Collections.Generic;
  30. using System.Diagnostics;
  31. using System.Runtime.InteropServices;
  32. using System.Threading;
  33. using System.Threading.Tasks;
  34. namespace System.Collections.Concurrent
  35. {
  36. [ComVisible (false)]
  37. [DebuggerDisplay ("Count={Count}")]
  38. [DebuggerTypeProxy (typeof (CollectionDebuggerView<>))]
  39. public class ConcurrentBag<T> : IProducerConsumerCollection<T>, IEnumerable<T>, IEnumerable
  40. {
  41. int size = Environment.ProcessorCount + 1;
  42. int multiplier = 2;
  43. int count;
  44. CyclicDeque<T>[] container;
  45. object syncLock = new object ();
  46. public ConcurrentBag ()
  47. {
  48. container = new CyclicDeque<T>[size];
  49. for (int i = 0; i < container.Length; i++)
  50. container[i] = new CyclicDeque<T> ();
  51. }
  52. public ConcurrentBag (IEnumerable<T> enumerable) : this ()
  53. {
  54. foreach (T item in enumerable)
  55. Add (item);
  56. }
  57. public bool TryAdd (T item)
  58. {
  59. Add (item);
  60. return true;
  61. }
  62. public void Add (T item)
  63. {
  64. Interlocked.Increment (ref count);
  65. GrowIfNecessary ();
  66. CyclicDeque<T> bag = GetBag ();
  67. bag.PushBottom (item);
  68. }
  69. public bool TryTake (out T item)
  70. {
  71. item = default (T);
  72. CyclicDeque<T> bag = GetBag ();
  73. if (bag == null || bag.PopBottom (out item) != PopResult.Succeed) {
  74. for (int i = 0; i < container.Length; i++) {
  75. if (container[i].PopTop (out item) == PopResult.Succeed) {
  76. Interlocked.Decrement (ref count);
  77. return true;
  78. }
  79. }
  80. } else {
  81. Interlocked.Decrement (ref count);
  82. return true;
  83. }
  84. return false;
  85. }
  86. public int Count {
  87. get {
  88. return count;
  89. }
  90. }
  91. public bool IsEmpty {
  92. get {
  93. return count == 0;
  94. }
  95. }
  96. object System.Collections.ICollection.SyncRoot {
  97. get {
  98. return this;
  99. }
  100. }
  101. bool System.Collections.ICollection.IsSynchronized {
  102. get {
  103. return true;
  104. }
  105. }
  106. IEnumerator IEnumerable.GetEnumerator ()
  107. {
  108. return GetEnumeratorInternal ();
  109. }
  110. IEnumerator<T> IEnumerable<T>.GetEnumerator ()
  111. {
  112. return GetEnumeratorInternal ();
  113. }
  114. IEnumerator<T> GetEnumeratorInternal ()
  115. {
  116. for (int i = 0; i < size; i++) {
  117. CyclicDeque<T> bag = container[i];
  118. foreach (T item in bag.GetEnumerable ()) {
  119. yield return item;
  120. }
  121. }
  122. }
  123. void System.Collections.ICollection.CopyTo (Array array, int index)
  124. {
  125. T[] a = array as T[];
  126. if (a == null)
  127. return;
  128. CopyTo (a, index);
  129. }
  130. public void CopyTo (T[] array, int index)
  131. {
  132. int c = count;
  133. if (array.Length < c + index)
  134. throw new InvalidOperationException ("Array is not big enough");
  135. CopyTo (array, index, c);
  136. }
  137. void CopyTo (T[] array, int index, int num)
  138. {
  139. int i = index;
  140. foreach (T item in this) {
  141. if (i >= num)
  142. break;
  143. array[i++] = item;
  144. }
  145. }
  146. public T[] ToArray ()
  147. {
  148. int c = count;
  149. T[] temp = new T[c];
  150. CopyTo (temp, 0, c);
  151. return temp;
  152. }
  153. int GetIndex ()
  154. {
  155. return Thread.CurrentThread.ManagedThreadId - 1;
  156. }
  157. void GrowIfNecessary ()
  158. {
  159. int index = GetIndex ();
  160. int currentSize = size;
  161. while (index > currentSize - 1) {
  162. currentSize = size;
  163. Grow (currentSize);
  164. }
  165. }
  166. CyclicDeque<T> GetBag ()
  167. {
  168. int i = GetIndex ();
  169. return i < container.Length ? container[i] : null;
  170. }
  171. void Grow (int referenceSize)
  172. {
  173. lock (syncLock) {
  174. if (referenceSize != size)
  175. return;
  176. CyclicDeque<T>[] slice = new CyclicDeque<T>[size * multiplier];
  177. int i = 0;
  178. for (i = 0; i < container.Length; i++)
  179. slice[i] = container[i];
  180. for (; i < slice.Length; i++)
  181. slice[i] = new CyclicDeque<T> ();
  182. container = slice;
  183. size = slice.Length;
  184. }
  185. }
  186. }
  187. }
  188. #endif