|
using System;
using System.Collections.Generic;
using System.Threading;
using Pfz.Caching;
using Pfz.Extensions.MonitorLockExtensions;
namespace Pfz.Threading
{
/// <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()
{
try
{
int minimumCollectionNumber = GC.CollectionCount(GC.MaxGeneration);
List<Parameters> freeEvents;
AbortSafe.UnabortableLock
(
fLock,
delegate
{
freeEvents = fFreeEvents;
var newFreeEvents = new List<Parameters>();
foreach(Parameters p in freeEvents)
{
if (p.KeepAliveUntilCollectionOfNumber >= minimumCollectionNumber)
newFreeEvents.Add(p);
else
{
p.Action = null;
p.WaitEvent.Set();
}
}
fFreeEvents = newFreeEvents;
}
);
}
catch
{
}
}
#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>
/// Runs an action in another thread. Uses an existing thread if one is
/// available or creates a new one if none are available, so this call will
/// not hang if there are no available threads.
/// </summary>
/// <param name="action">The function to execute.</param>
public static void Run(Action action)
{
Run
(
delegate(object obj)
{
action();
},
null
);
}
/// <summary>
/// Runs an action in another thread. Uses an existing thread if one is
/// available or creates a new one if none are available, so this call will
/// not hang if there are no available threads.
/// </summary>
/// <param name="action">The function to execute.</param>
/// <param name="parameter">The object passed as parameter to the action.</param>
public static void Run(Action<object> action, object parameter)
{
if (action == null)
throw new ArgumentNullException("action");
Parameters p = null;
fLock.LockWithTimeout
(
delegate
{
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.Action = action;
p.Parameter = parameter;
p.Thread.IsBackground = false;
p.WaitEvent.Set();
}
/// <summary>
/// Runs an action in another thread. Uses an existing thread if one is
/// available or creates a new one if none are available, so this call will
/// not hang if there are no available threads.
/// </summary>
/// <typeparam name="T">The type of the parameter.</typeparam>
/// <param name="action">The function to execute.</param>
/// <param name="parameter">The object passed as parameter to the action.</param>
public static void Run<T>(Action<T> action, T parameter)
{
Run
(
delegate(object obj)
{
T typedParameter = (T)obj;
action(typedParameter);
},
parameter
);
}
#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.Action == null)
{
waitEvent.Close();
return;
}
p.Action(p.Parameter);
fLock.LockWithTimeout
(
delegate
{
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)
{
fLock.LockWithTimeout
(
() => fFreeEvents.Remove(p)
);
}
}
}
#endregion
#endregion
#region Parameters - Nested class
private sealed class Parameters
{
internal Thread Thread;
internal ManualResetEvent WaitEvent;
internal volatile Action<object> Action;
internal volatile object Parameter;
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).