1  //--------------------------------------------------------------------------
  2  // 
  3  //  Copyright (c) Microsoft Corporation.  All rights reserved. 
  4  // 
  5  //  File: ReductionVariable.cs
  6  //
  7  //--------------------------------------------------------------------------
  8  
  9  using System.Collections.Concurrent;
 10  using System.Collections.Generic;
 11  using System.Diagnostics;
 12  using System.Linq;
 13  using System.Runtime.CompilerServices;
 14  
 15  namespace System.Threading
 16  {
 17      /// <summary>Provides a reduction variable for aggregating data across multiple threads involved in a computation.</summary>
 18      /// <typeparam name="T">Specifies the type of the data being aggregated.</typeparam>
 19      [DebuggerDisplay("Count={_values.Count}")]
 20      [DebuggerTypeProxy(typeof(ReductionVariable_DebugView<>))]
 21      public sealed class ReductionVariable<T>
 22      {
 23          /// <summary>The factory used to initialize a value on a thread.</summary>
 24          private readonly Func<T> _seedFactory;
 25          /// <summary>Thread-local storage for each thread's value.</summary>
 26          private readonly ThreadLocal<StrongBox<T>> _threadLocal;
 27          /// <summary>The list of all thread-local values for later enumeration.</summary>
 28          private readonly ConcurrentQueue<StrongBox<T>> _values = new ConcurrentQueue<StrongBox<T>>();
 29  
 30          /// <summary>Initializes the instances.</summary>
 31          /// <param name="seedFactory">
 32          /// The function invoked to provide the initial value for a thread.  If
 33          /// null, the default value of T will be used as the seed.
 34          /// </param>
 35          public ReductionVariable(Func<T> seedFactory)
 36          {
 37              _seedFactory = seedFactory;
 38              _threadLocal = new ThreadLocal<StrongBox<T>>(CreateValue);
 39          }
 40  
 41          /// <summary>Creates a value for the current thread and stores it in the central list of values.</summary>
 42          /// <returns>The boxed value.</returns>
 43          private StrongBox<T> CreateValue()
 44          {
 45              var s = new StrongBox<T>(_seedFactory != null ? _seedFactory() : default(T));
 46              _values.Enqueue(s);
 47              return s;
 48          }
 49  
 50          /// <summary>Gets or sets the value for the current thread.</summary>
 51          public T Value
 52          {
 53              get { return _threadLocal.Value.Value; }
 54              set { _threadLocal.Value.Value = value; }
 55          }
 56  
 57          /// <summary>Gets the values for all of the threads that have used this instance.</summary>
 58          public IEnumerable<T> Values { get { return _values.Select(s => s.Value); } }
 59  
 60          /// <summary>Applies an accumulator function over the values in this variable.</summary>
 61          /// <param name="function">An accumulator function to be invoked on each value.</param>
 62          /// <returns>The accumulated value.</returns>
 63          public T Reduce(Func<T, T, T> function)
 64          {
 65              return Values.Aggregate(function);
 66          }
 67  
 68          /// <summary>
 69          /// Applies an accumulator function over the values in this variable.
 70          /// The specified seed is used as the initial accumulator value.
 71          /// </summary>
 72          /// <param name="function">An accumulator function to be invoked on each value.</param>
 73          /// <returns>The accumulated value.</returns>
 74          public TAccumulate Reduce<TAccumulate>(TAccumulate seed, Func<TAccumulate, T, TAccumulate> function)
 75          {
 76              return Values.Aggregate(seed, function);
 77          }
 78      }
 79  
 80      /// <summary>Debug view for the reductino variable</summary>
 81      /// <typeparam name="T">Specifies the type of the data being aggregated.</typeparam>
 82      internal sealed class ReductionVariable_DebugView<T>
 83      {
 84          private ReductionVariable<T> _variable;
 85  
 86          public ReductionVariable_DebugView(ReductionVariable<T> variable)
 87          {
 88              _variable = variable;
 89          }
 90  
 91          [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
 92          public T[] Values { get { return _variable.Values.ToArray(); } }
 93      }
 94  }