andrew4582

Resource Pool

Oct 27th, 2012
206
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. using System;
  2. using System.Linq;
  3. using System.Collections.Concurrent;
  4. using System.Diagnostics;
  5. using System.Threading;
  6. using Core.Threading;
  7.  
  8. namespace ConsoleApplication1
  9. {
  10.     class ResourcePoolExample
  11.     {
  12.  
  13.         static void Main()
  14.         {
  15.            
  16.             ExpensiveResource.InstanceCount = 0;
  17.  
  18.             int threadCount = 25;
  19.             int maxInstances = 2;
  20.  
  21.             int min = threadCount- 2;
  22.            
  23.             ThreadPool.SetMinThreads(min, min);
  24.             //create factory to create ExpensiveResource instances
  25.             Func<ExpensiveResource> factory = () =>
  26.             {
  27.                 return new ExpensiveResource();
  28.             };
  29.  
  30.             ResourcePool<ExpensiveResource> pool = new ResourcePool<ExpensiveResource>(maxInstances, factory, 5000);
  31.  
  32.             //event to signal when all threads are started
  33.             CountdownEvent allStarted = new CountdownEvent(threadCount);
  34.  
  35.             //event to signal when all threads are complete
  36.             CountdownEvent allEnded = new CountdownEvent(threadCount);
  37.  
  38.             //event to signal all threads to go
  39.             ManualResetEventSlim goevent = new ManualResetEventSlim(false);
  40.  
  41.             //Action that runs in every thread
  42.             Action<int> testAction = (id) =>
  43.             {
  44.  
  45.                 string indent = "".PadLeft(id, '-');
  46.                
  47.                 //signal this thread has started
  48.                 allStarted.Signal();
  49.                 Console.Write(".");
  50.                 //wait for every thread
  51.                 goevent.Wait();
  52.  
  53.                 //delay each thread to simulate reality.
  54.                 Random rnd = new Random(Guid.NewGuid().ToString().GetHashCode());
  55.                 Thread.Sleep(rnd.Next(10, 100));
  56.  
  57.                 ExpensiveResource resource = null;
  58.  
  59.                 try
  60.                 {
  61.  
  62.                     //we grab our resource
  63.                     resource = pool.Take();
  64.  
  65.                     Console.WriteLine(indent + "Action{0} Take -> Instance_{1}", id, resource.InstanceId);
  66.  
  67.                     //execute process
  68.                     resource.ExpensiveProcess();
  69.  
  70.                 }
  71.                 catch (Exception error)
  72.                 {
  73.                     Console.WriteLine("ERROR ExpensiveProcess: {0}", error.Message);
  74.                 }
  75.                 finally
  76.                 {
  77.                     //return our resource to the pool when finished
  78.                     pool.Return(resource);
  79.                     Console.WriteLine(indent + "Action{0} Return -> Instance_{1}", id, resource.InstanceId);
  80.  
  81.                     //signal when finished
  82.                     allEnded.Signal();
  83.                 }
  84.             };
  85.  
  86.             Console.WriteLine("Starting {0:N0} threads", threadCount);
  87.  
  88.             //create our worker threads
  89.             for (int i = 0; i < threadCount; i++)
  90.             {
  91.                 int workerId = i + 1;
  92.  
  93.                 ThreadPool.QueueUserWorkItem((state) =>
  94.                 {
  95.                     testAction((int)state);
  96.                 }, workerId);
  97.  
  98.                
  99.             }
  100.             Console.WriteLine();
  101.  
  102.             //wait for all threads  to startup
  103.             allStarted.Wait();
  104.             Console.WriteLine();
  105.             Console.WriteLine("All {0:N0} threads have started",threadCount);
  106.             //fire off all threads
  107.             goevent.Set();
  108.  
  109.             //wait until all threads are completed
  110.             allEnded.Wait();
  111.  
  112.             //uncomment to use 'Parallel lib'
  113.             //Parallel.For(0,threadCount,testAction);
  114.  
  115.             Console.WriteLine();
  116.             Console.WriteLine("threadCount {0}", threadCount);
  117.             Console.WriteLine("pool.MaxInstances: {0}", pool.MaxInstances);
  118.             Console.WriteLine("pool.InstancesCreated: {0}", pool.InstancesCreated);
  119.             Console.WriteLine("ExpensiveResource.InstanceCount: {0}", ExpensiveResource.InstanceCount);
  120.             Console.WriteLine("pool.Count: {0}", pool.Count);
  121.             Console.WriteLine();
  122.             Console.WriteLine("Test Finished");
  123.         }
  124.     }
  125.     class ExpensiveResource
  126.     {
  127.  
  128.         //global instances created
  129.         public static int InstanceCount;
  130.  
  131.         //resource id
  132.         public int InstanceId { get; private set; }
  133.  
  134.         public ExpensiveResource()
  135.         {
  136.  
  137.             InstanceId = Interlocked.Increment(ref InstanceCount);
  138.             Console.WriteLine("Created Instance #: {0}", InstanceId);
  139.         }
  140.  
  141.         public void ExpensiveProcess()
  142.         {
  143.             //simulate long process
  144.             Random rnd = new Random(Guid.NewGuid().ToString().GetHashCode());
  145.             int st = rnd.Next(500, 1000);
  146.             Thread.Sleep(st);
  147.         }
  148.     }
  149. }
  150.  
  151.  
  152. namespace Core.Threading {
  153.  
  154.     /// <summary>
  155.     /// Resource pool that uses a <see cref="System.Collections.Concurrent.BlockingCollection<T>"/> that limits the use of specified resource.
  156.     /// If an item in the pool is requested and the instance count is under the maximum a new instance of the resource is created via <see cref="Func<T>"/> parameter.
  157.     /// </summary>
  158.     /// <example>
  159.     /*
  160.          class ResourcePoolExample {
  161.          
  162.         static void Main() {
  163.  
  164.             ExpensiveResource.InstanceCount = 0;
  165.  
  166.             int threadCount = 8;
  167.             int maxInstances = 2;
  168.  
  169.             //create factory to create ExpensiveResource instances
  170.             Func<ExpensiveResource> factory = () => {
  171.                 return new ExpensiveResource();
  172.             };
  173.  
  174.             ResourcePool<ExpensiveResource> pool = new ResourcePool<ExpensiveResource>(maxInstances,factory,5000);
  175.            
  176.             //event to signal when all threads are complete
  177.             CountdownEvent cde = new CountdownEvent(threadCount);
  178.  
  179.             //event to signal all threads to go
  180.             ManualResetEventSlim goevent = new ManualResetEventSlim(false);
  181.  
  182.             //Action that runs in every thread
  183.             Action<int> testAction = (id) => {
  184.  
  185.                 string indent = "".PadLeft(id,'-');
  186.                
  187.                 //wait for every thread
  188.                 goevent.Wait();
  189.  
  190.                 //delay each thread to simulate reality.
  191.                 Random rnd = new Random(Guid.NewGuid().ToString().GetHashCode());
  192.                 Thread.Sleep(rnd.Next(10,100));
  193.  
  194.                 ExpensiveResource resource = null;
  195.  
  196.                 try {
  197.  
  198.                     //we grab our resource
  199.                     resource = pool.Take();
  200.  
  201.                     Console.WriteLine(indent + "Action{0} Take -> Instance_{1}",id,resource.InstanceId);
  202.  
  203.                     //execute process
  204.                     resource.ExpensiveProcess();
  205.  
  206.                 }
  207.                 catch(Exception error) {
  208.                     Console.WriteLine("ERROR ExpensiveProcess: {0}",error.Message);
  209.                 }
  210.                 finally {
  211.                     //return our resource to the pool when finished
  212.                     pool.Return(resource);
  213.                     Console.WriteLine(indent + "Action{0} Return -> Instance_{1}",id,resource.InstanceId);
  214.  
  215.                     //signal when finished
  216.                     cde.Signal();
  217.                 }
  218.             };
  219.  
  220.             //create our worker threads
  221.             for(int i = 0;i < threadCount;i++) {
  222.                 int workerId = i + 1;
  223.  
  224.                 ThreadPool.QueueUserWorkItem((state) => {
  225.                     testAction((int)state);
  226.                 },workerId);
  227.             }
  228.  
  229.             Thread.Sleep(10);
  230.      
  231.             //fire off all threads
  232.             goevent.Set();
  233.  
  234.             //wait until all threads are completed
  235.             cde.Wait();
  236.  
  237.             //uncomment to use 'Parallel lib'
  238.             //Parallel.For(0,threadCount,testAction);
  239.  
  240.             Console.WriteLine();
  241.             Console.WriteLine("threadCount {0}",threadCount);
  242.             Console.WriteLine("pool.MaxInstances: {0}",pool.MaxInstances);
  243.             Console.WriteLine("pool.InstancesCreated: {0}",pool.InstancesCreated);
  244.             Console.WriteLine("ExpensiveResource.InstanceCount: {0}",ExpensiveResource.InstanceCount);
  245.             Console.WriteLine("pool.Count: {0}",pool.Count);
  246.             Console.WriteLine();
  247.             Console.WriteLine("Test Finished");
  248.         }
  249.     }
  250.     class ExpensiveResource {
  251.  
  252.         //global instances created
  253.         public static int InstanceCount;
  254.  
  255.         //resource id
  256.         public int InstanceId { get; private set; }
  257.  
  258.         public ExpensiveResource() {
  259.            
  260.             InstanceId = Interlocked.Increment(ref InstanceCount);
  261.             Console.WriteLine("Created Instance #: {0}",InstanceId);
  262.         }
  263.  
  264.         public void ExpensiveProcess() {
  265.             //simulate long process
  266.             Random rnd = new Random(Guid.NewGuid().ToString().GetHashCode());
  267.             int st = rnd.Next(500,1000);
  268.             Thread.Sleep(st);
  269.         }
  270.     }
  271.      */
  272.     /// </example>
  273.     /// <typeparam name="T">class</typeparam>
  274.     public class ResourcePool<T> where T:class {
  275.  
  276.         protected readonly object _syncroot = new object();
  277.         protected readonly BlockingCollection<T> _items;
  278.         protected readonly Func<T> _factory;
  279.         readonly int _timeout;
  280.         volatile int _maxInstances;
  281.         volatile int _created;
  282.  
  283.         /// <summary>Instances that were created</summary>
  284.         public int InstancesCreated { get { return _created; } }
  285.  
  286.         /// <summary>Maximum instances that could be created</summary>
  287.         public int MaxInstances { get { return _maxInstances; } }
  288.  
  289.         /// <summary>Gets the number of current objects available in the pool</summary>
  290.         public int Count { get { return _items.Count; } }
  291.  
  292.         ///<summary>Gets the wait time if all resources are taken and no more can be created, default is -1 (infinity)</summary>
  293.         public int Timeout { get { return _timeout; } }
  294.  
  295.         /// <summary>
  296.         /// ctr
  297.         /// </summary>
  298.         /// <param name="maxInstances">Maximum number of instances that are to be created</param>
  299.         /// <param name="factory">Factory function to create an instance of the specified Type </param>
  300.         /// <param name="timeout">The wait time if all resources are taken and no more can be created, default is -1 (infinity)</param>
  301.         public ResourcePool(int maxInstances,Func<T> factory,int timeout = -1) {
  302.  
  303.             if(factory == null)
  304.                 throw new ArgumentNullException("factory");
  305.  
  306.             this._maxInstances = Math.Max(1,maxInstances);
  307.             this._items = new BlockingCollection<T>(_maxInstances);
  308.             this._factory = factory;
  309.             this._timeout = timeout;
  310.         }
  311.  
  312.         /// <summary>Increments the MaxInstances value</summary>
  313.         public void IncrementMaxInstances(int count) {
  314.             _maxInstances += count;
  315.         }
  316.  
  317.         protected virtual void OnCreated(T instance) {
  318.             return;
  319.         }
  320.         protected virtual bool CanReturn(T instance) {
  321.             return true;
  322.         }
  323.  
  324.         /// <summary>
  325.         /// Returns an object back to the resource pool to be available the next time <see cref="Take"/> is called
  326.         /// </summary>
  327.         /// <param name="item">The object to be placed back into the pool</param>
  328.         /// <returns>True if object is added back to the pool, otherwise false</returns>
  329.         public virtual bool Return(T item) {
  330.             if(item == null)
  331.                 return false;
  332.  
  333.             bool added = false;
  334.             if(CanReturn(item)) {
  335.                 added = _items.TryAdd(item);
  336.             }
  337. #if DEBUG
  338.             Debug.WriteLine("Return-> hash: {0} - added: {1}",item.GetHashCode(),added);
  339. #endif
  340.             return added;
  341.         }
  342.  
  343.         /// <summary>
  344.         /// Either takes an existing object from the pool, or it creates a new instance if the <see cref="InstancesCreated"/> have not exceed the <see cref="MaxInstances"/> value.
  345.         /// </summary>
  346.         /// <returns>An existing object from the pool or a new instnace</returns>
  347.         public virtual T Take() {
  348.  
  349. #if DEBUG
  350.             int hash = -1;
  351.             bool added = false;
  352.              
  353. #endif
  354.             bool created = false;
  355.             T item = null;
  356.  
  357.             //take out the resource immediately
  358.             if(_items.TryTake(out item,0))
  359.                 return item;
  360.  
  361.             lock(_syncroot) {
  362.  
  363.                 //create resource if we can
  364.                 if(_created < _maxInstances) {
  365.  
  366.                     item = _factory();
  367.  
  368.                     _created++;
  369.                     _items.Add(item);
  370.                     created = true;
  371.                 }
  372.             }
  373.  
  374.             if(created) {
  375.                 OnCreated(item);
  376.                 return item;
  377.             }
  378.  
  379.             if(_created >= _maxInstances) {
  380.  
  381.                 //all resources are created, wait until there is one available
  382.                 if(!_items.TryTake(out item,_timeout))
  383.                     //we timed out
  384.                     throw new TimeoutException();
  385.             }
  386.  
  387. #if DEBUG
  388.             if(item != null)
  389.                 hash = item.GetHashCode();
  390.              
  391. #endif
  392.             return item;
  393.         }
  394.     }
  395. }
Advertisement
Add Comment
Please, Sign In to add comment