|
using System;
using System.Collections.Generic;
using System.Threading;
using Pfz.Caching;
namespace Pfz
{
/// <summary>
/// This is a thread pool that, different from System.Threading.ThreadPool,
/// does not have a thread limit and the threads are not background while
/// they run. Comparing to the system ThreadPool, it is better if the
/// thread can enter in wait mode. This happens when one thread has a
/// "fast" process, but can only terminate after another thread returns.
///
/// This thread pool keep threads alive for a certain number of generations.
/// The default value is two. So, at the first use, it lives for one more
/// generation. After the second use in that generation, it is marked to
/// live for two generations. Subsequent uses in this generation will not
/// change anything. Do not use very high values, as this can cause memory
/// usage problems.
/// </summary>
public static class UnlimitedThreadPool
{
#region Private fields
private static object fLock = new object();
private static volatile List<Parameters> fFreeEvents = new List<Parameters>();
#endregion
#region Constructor
static UnlimitedThreadPool()
{
GCUtils.Collected += p_Collected;
}
#endregion
#region p_Collected
private static void p_Collected()
{
GCUtils.Collected += p_Collected;
int minimumCollectionNumber = GC.CollectionCount(GC.MaxGeneration);
List<Parameters> freeEvents;
lock(fLock)
{
freeEvents = fFreeEvents;
var newFreeEvents = new List<Parameters>();
fFreeEvents = newFreeEvents;
foreach(Parameters p in freeEvents)
{
if (p.KeepAliveUntilCollectionOfNumber >= minimumCollectionNumber)
newFreeEvents.Add(p);
else
{
p.Run = null;
p.WaitEvent.Set();
}
}
}
}
#endregion
#region Properties
#region CollectionsToKeepAlive
private static volatile int fCollectionsToKeepAlive = 2;
/// <summary>
/// Gets or sets the maximum number of collections to keep a Thread from this pool
/// alive. The default value is two.
/// </summary>
public static int CollectionsToKeepAlive
{
get
{
return fCollectionsToKeepAlive;
}
set
{
if (value <= 0)
throw new ArgumentException("value must be greater than zero.", "UnlimitedThreadPool.CollectionsToKeepAlive");
fCollectionsToKeepAlive = value;
}
}
#endregion
#endregion
#region Methods
#region Run
/// <summary>
/// Queues an user item. In fact, the item will be executed in a new thread if no available thread
/// exists.
/// </summary>
/// <param name="handler">The function to execute.</param>
public static void Run(ParameterizedThreadStart handler)
{
Run(handler, null);
}
/// <summary>
/// Queues an user item. In fact, the item will be executed in a new thread if no available thread
/// exists.
/// </summary>
/// <param name="handler">The function to execute.</param>
/// <param name="state">The object passed as parameter to the function.</param>
public static void Run(ParameterizedThreadStart handler, object state)
{
if (handler == null)
throw new ArgumentNullException("handler");
Parameters p = null;
lock(fLock)
{
int count = fFreeEvents.Count;
if (count > 0)
{
int index = count - 1;
p = fFreeEvents[index];
fFreeEvents.RemoveAt(index);
}
}
if (p == null)
{
p = new Parameters();
p.WaitEvent = new ManualResetEvent(false);
Thread thread = new Thread(p_RunThread);
p.Thread = thread;
thread.Start(p);
}
p.Run = handler;
p.State = state;
p.Thread.IsBackground = false;
p.WaitEvent.Set();
}
#endregion
#region p_RunThread
private static void p_RunThread(object parameters)
{
Thread currentThread = Thread.CurrentThread;
Parameters p = (Parameters)parameters;
ManualResetEvent waitEvent = p.WaitEvent;
try
{
while(true)
{
waitEvent.WaitOne();
if (p.Run == null)
{
waitEvent.Close();
return;
}
p.Run(p.State);
lock(fLock)
{
int actualCollectionNumber = GC.CollectionCount(GC.MaxGeneration);
int keepAliveUntil = p.KeepAliveUntilCollectionOfNumber;
if (keepAliveUntil <= actualCollectionNumber)
p.KeepAliveUntilCollectionOfNumber = actualCollectionNumber + 1;
else
{
int maxValue = actualCollectionNumber + fCollectionsToKeepAlive;
if (keepAliveUntil < maxValue)
p.KeepAliveUntilCollectionOfNumber = keepAliveUntil + 1;
}
currentThread.IsBackground = true;
waitEvent.Reset();
fFreeEvents.Add(p);
}
}
}
finally
{
if (currentThread.IsBackground)
lock(fLock)
fFreeEvents.Remove(p);
}
}
#endregion
#endregion
#region Parameters - Nested class
private sealed class Parameters
{
internal Thread Thread;
internal ManualResetEvent WaitEvent;
internal volatile ParameterizedThreadStart Run;
internal volatile object State;
internal volatile int KeepAliveUntilCollectionOfNumber;
}
#endregion
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
I started to program computers when I was 11 years old, as a hobbyist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.
At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do their work easier, faster and with less errors.
Want more info or simply want to contact me?
Take a look at:
http://paulozemek.azurewebsites.net/
Or e-mail me at: paulozemek@outlook.com
Codeproject MVP 2012, 2015 & 2016
Microsoft MVP 2013-2014 (in October 2014 I started working at Microsoft, so I can't be a Microsoft MVP anymore).