18 changed files with 1534 additions and 507 deletions
File diff suppressed because it is too large
@ -0,0 +1,147 @@ |
|||
// <copyright file="CommonParallel.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://mathnet.opensourcedotnet.info
|
|||
// Copyright (c) 2009-2010 Math.NET
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.Threading |
|||
{ |
|||
using System; |
|||
|
|||
#if !SILVERLIGHT
|
|||
using System.Collections.Concurrent; |
|||
using System.Threading.Tasks; |
|||
#endif
|
|||
|
|||
/// <summary>
|
|||
/// Used to simplify parallel code, particularly between the .NET 4.0 and Silverlight Code.
|
|||
/// </summary>
|
|||
internal static class CommonParallel |
|||
{ |
|||
/// <summary>
|
|||
/// Executes a for loop in which iterations may run in parallel.
|
|||
/// </summary>
|
|||
/// <param name="fromInclusive">The start index, inclusive.</param>
|
|||
/// <param name="toExclusive">The end index, exclusive.</param>
|
|||
/// <param name="body">The body to be invoked for each iteration.</param>
|
|||
/// <exception cref="ArgumentNullException">The <paramref name="body"/> argument is null.</exception>
|
|||
/// <exception cref="AggregateException">At least one invocation of the body threw an exception.</exception>
|
|||
public static void For(int fromInclusive, int toExclusive, Action<int> body) |
|||
{ |
|||
#if SILVERLIGHT
|
|||
Parallel.For(fromInclusive, toExclusive, body); |
|||
#else
|
|||
if (Control.DisableParallelization || Control.NumberOfParallelWorkerThreads < 2) |
|||
{ |
|||
for (var index = fromInclusive; index < toExclusive; index++) |
|||
{ |
|||
body(index); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
Parallel.ForEach( |
|||
Partitioner.Create(fromInclusive, toExclusive), |
|||
new ParallelOptions { MaxDegreeOfParallelism = Control.NumberOfParallelWorkerThreads }, |
|||
(range, loopState) => |
|||
{ |
|||
for (var i = range.Item1; i < range.Item2; i++) |
|||
{ |
|||
body(i); |
|||
} |
|||
}); |
|||
} |
|||
#endif
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Aggregates a function over a loop.
|
|||
/// </summary>
|
|||
/// <param name="fromInclusive">Starting index of the loop.</param>
|
|||
/// <param name="toExclusive">Ending index of the loop</param>
|
|||
/// <param name="body">The function to aggregate.</param>
|
|||
/// <returns>The sum of the function over the loop.</returns>
|
|||
public static double Aggregate(int fromInclusive, int toExclusive, Func<int, double> body) |
|||
{ |
|||
var sync = new object(); |
|||
var sum = 0.0; |
|||
|
|||
#if SILVERLIGHT
|
|||
Parallel.For( |
|||
fromInclusive, |
|||
toExclusive, |
|||
() => 0.0, |
|||
(i, localData) => localData += body(i), |
|||
localResult => |
|||
{ |
|||
lock (sync) |
|||
{ |
|||
sum += localResult; |
|||
} |
|||
}); |
|||
#else
|
|||
|
|||
if (Control.DisableParallelization || Control.NumberOfParallelWorkerThreads < 2) |
|||
{ |
|||
for (var index = fromInclusive; index < toExclusive; index++) |
|||
{ |
|||
sum += body(index); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
Parallel.ForEach( |
|||
Partitioner.Create(fromInclusive, toExclusive), |
|||
new ParallelOptions { MaxDegreeOfParallelism = Control.NumberOfParallelWorkerThreads }, |
|||
() => 0.0, |
|||
(range, loopState, localData) => |
|||
{ |
|||
for (var i = range.Item1; i < range.Item2; i++) |
|||
{ |
|||
localData += body(i); |
|||
} |
|||
|
|||
return localData; |
|||
}, |
|||
localResult => |
|||
{ |
|||
lock (sync) |
|||
{ |
|||
sum += localResult; |
|||
} |
|||
}); |
|||
} |
|||
#endif
|
|||
return sum; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Executes each of the provided actions inside a discrete, asynchronous task.
|
|||
/// </summary>
|
|||
/// <param name="actions">An array of actions to execute.</param>
|
|||
/// <exception cref="ArgumentException">The actions array contains a null element.</exception>
|
|||
/// <exception cref="AggregateException">An action threw an exception.</exception>
|
|||
public static void Invoke(params Action[] actions) |
|||
{ |
|||
Parallel.Invoke(actions); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
// <copyright file="AggregateException.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://mathnet.opensourcedotnet.info
|
|||
// Copyright (c) 2009 Math.NET
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.Threading |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.ObjectModel; |
|||
|
|||
/// <summary>
|
|||
/// Represents multiple errors that occur during application execution.
|
|||
/// </summary>
|
|||
public class AggregateException : Exception |
|||
{ |
|||
/// <summary>
|
|||
/// List of the aggregated exceptions.
|
|||
/// </summary>
|
|||
private readonly IList<Exception> _exceptions = new List<Exception>(); |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the AggregateException class with a specified error message and references to the inner exceptions that are the cause of this exception.
|
|||
/// </summary>
|
|||
/// <param name="exceptions">The exceptions that are the cause of the current exception.</param>
|
|||
public AggregateException(IEnumerable<Exception> exceptions) |
|||
{ |
|||
foreach (var exception in exceptions) |
|||
{ |
|||
this._exceptions.Add(exception); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a read-only collection of the Exception instances that caused the current exception.
|
|||
/// </summary>
|
|||
/// <value>A read-only collection of the Exception instances that caused the current exception</value>
|
|||
public ReadOnlyCollection<Exception> InnerExceptions |
|||
{ |
|||
get |
|||
{ |
|||
return new ReadOnlyCollection<Exception>(this._exceptions); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,485 @@ |
|||
// <copyright file="Parallel.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://mathnet.opensourcedotnet.info
|
|||
//
|
|||
// Copyright (c) 2009 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.Threading |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Properties; |
|||
|
|||
/// <summary>
|
|||
/// Provides support for parallel loops.
|
|||
/// </summary>
|
|||
internal static class Parallel |
|||
{ |
|||
/// <summary>
|
|||
/// The amount to scale the foreach buffer after each iteration.
|
|||
/// </summary>
|
|||
private const int ScalingFactor = 2; |
|||
|
|||
/// <summary>
|
|||
/// The maximum size of the foreach buffer.
|
|||
/// </summary>
|
|||
private const int MaxBlockSize = 65536; |
|||
|
|||
/// <summary>
|
|||
/// The initial size of the for each buffer.
|
|||
/// </summary>
|
|||
private const int IntialBlockSize = 1024; |
|||
|
|||
/// <summary>
|
|||
/// Executes a for loop in which iterations may run in parallel.
|
|||
/// </summary>
|
|||
/// <param name="fromInclusive">The start index, inclusive.</param>
|
|||
/// <param name="toExclusive">The end index, exclusive.</param>
|
|||
/// <param name="body">The body to be invoked for each iteration.</param>
|
|||
/// <exception cref="ArgumentNullException">The <paramref name="body"/> argument is null.</exception>
|
|||
/// <exception cref="AggregateException">At least one invocation of the body threw an exception.</exception>
|
|||
public static void For(int fromInclusive, int toExclusive, Action<int> body) |
|||
{ |
|||
if (body == null) |
|||
{ |
|||
throw new ArgumentNullException("body"); |
|||
} |
|||
|
|||
// fast forward execution if it's only one or none items
|
|||
var count = toExclusive - fromInclusive; |
|||
if (count <= 1) |
|||
{ |
|||
if (count == 1) |
|||
{ |
|||
body(fromInclusive); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
// fast forward execution in case parallelization is disabled
|
|||
if (Control.DisableParallelization |
|||
|| ThreadQueue.ThreadCount <= 1 |
|||
|| ThreadQueue.IsInWorkerThread) |
|||
{ |
|||
for (int i = fromInclusive; i < toExclusive; i++) |
|||
{ |
|||
body(i); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
var actions = new Action[ThreadQueue.ThreadCount]; |
|||
var size = count / actions.Length; |
|||
|
|||
// partition the jobs into separate sets for each but the last worked thread
|
|||
for (var i = 0; i < actions.Length - 1; i++) |
|||
{ |
|||
var start = fromInclusive + (i * size); |
|||
var stop = fromInclusive + ((i + 1) * size); |
|||
|
|||
actions[i] = |
|||
() => |
|||
{ |
|||
for (int j = start; j < stop; j++) |
|||
{ |
|||
body(j); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
// add another set for last worker thread
|
|||
actions[actions.Length - 1] = |
|||
() => |
|||
{ |
|||
for (int i = fromInclusive + ((actions.Length - 1) * size); i < toExclusive; i++) |
|||
{ |
|||
body(i); |
|||
} |
|||
}; |
|||
|
|||
Invoke(actions); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Executes a for loop in which iterations may run in parallel.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the thread-local data.</typeparam>
|
|||
/// <param name="fromInclusive">The start index, inclusive.</param>
|
|||
/// <param name="toExclusive">The end index, exclusive.</param>
|
|||
/// <param name="localInit">The function delegate that returns the initial state of the local data for each thread.</param>
|
|||
/// <param name="body">The delegate that is invoked once per iteration.</param>
|
|||
/// <param name="localFinally">The delegate that performs a final action on the local state of each thread.</param>
|
|||
public static void For<T>(int fromInclusive, int toExclusive, Func<T> localInit, Func<int, T, T> body, Action<T> localFinally) |
|||
{ |
|||
var count = toExclusive - fromInclusive; |
|||
var tasks = new Task<T>[ThreadQueue.ThreadCount]; |
|||
var size = count / tasks.Length; |
|||
|
|||
// fast forward execution if it's only one or none items
|
|||
if (count <= 1) |
|||
{ |
|||
if (count == 1) |
|||
{ |
|||
localFinally(body(fromInclusive, localInit())); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
// fast forward execution in case parallelization is disabled
|
|||
if (Control.DisableParallelization |
|||
|| ThreadQueue.ThreadCount <= 1 |
|||
|| ThreadQueue.IsInWorkerThread) |
|||
{ |
|||
var localresult = localInit(); |
|||
for (var i = fromInclusive; i < toExclusive; i++) |
|||
{ |
|||
localresult = body(i, localresult); |
|||
} |
|||
|
|||
localFinally(localresult); |
|||
return; |
|||
} |
|||
|
|||
// partition the jobs into separate sets for each but the last worked thread
|
|||
for (var i = 0; i < tasks.Length - 1; i++) |
|||
{ |
|||
var start = fromInclusive + (i * size); |
|||
var stop = fromInclusive + ((i + 1) * size); |
|||
tasks[i] = new Task<T>( |
|||
localData => |
|||
{ |
|||
var localresult = (T)localData; |
|||
for (var j = start; j < stop; j++) |
|||
{ |
|||
localresult = body(j, localresult); |
|||
} |
|||
|
|||
return localresult; |
|||
}, |
|||
localInit()); |
|||
ThreadQueue.Enqueue(tasks[i]); |
|||
} |
|||
|
|||
// add another set for last worker thread
|
|||
tasks[tasks.Length - 1] = new Task<T>( |
|||
localData => |
|||
{ |
|||
var localresult = (T)localData; |
|||
for (var i = fromInclusive + ((tasks.Length - 1) * size); i < toExclusive; i++) |
|||
{ |
|||
localresult = body(i, localresult); |
|||
} |
|||
|
|||
return localresult; |
|||
}, |
|||
localInit()); |
|||
|
|||
ThreadQueue.Enqueue(tasks[tasks.Length - 1]); |
|||
if (tasks.Length <= 0) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
WaitForTasksToComplete(tasks); |
|||
|
|||
foreach (var t in tasks) |
|||
{ |
|||
localFinally(t.Result); |
|||
} |
|||
|
|||
CollectExceptions(tasks); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Executes a for each operation on an IEnumerable{T} in which iterations may run in parallel.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the data in the source.</typeparam>
|
|||
/// <param name="source">An enumerable data source.</param>
|
|||
/// <param name="body">The delegate that is invoked once per iteration.</param>
|
|||
public static void ForEach<T>(IEnumerable<T> source, Action<T> body) |
|||
{ |
|||
if (body == null) |
|||
{ |
|||
throw new ArgumentNullException("body"); |
|||
} |
|||
|
|||
// fast forward execution in case parallelization is disabled
|
|||
if (Control.DisableParallelization |
|||
|| ThreadQueue.ThreadCount <= 1 |
|||
|| ThreadQueue.IsInWorkerThread) |
|||
{ |
|||
foreach (var item in source) |
|||
{ |
|||
body(item); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
// source is a IList, call For instead.
|
|||
if (source is IList<T>) |
|||
{ |
|||
var list = (IList<T>)source; |
|||
For(0, list.Count, i => body(list[i])); |
|||
return; |
|||
} |
|||
|
|||
var maxBlockSize = IntialBlockSize; |
|||
var tasks = new List<Task>(); |
|||
|
|||
var enumerator = source.GetEnumerator(); |
|||
while (enumerator.MoveNext()) |
|||
{ |
|||
var pos = 0; |
|||
var list = new T[maxBlockSize]; |
|||
list[pos++] = enumerator.Current; |
|||
|
|||
var count = 1; |
|||
while (count < maxBlockSize && enumerator.MoveNext()) |
|||
{ |
|||
list[pos++] = enumerator.Current; |
|||
count++; |
|||
} |
|||
|
|||
var task = new Task( |
|||
() => |
|||
{ |
|||
for (var i = 0; i < pos; i++) |
|||
{ |
|||
body(list[i]); |
|||
} |
|||
}); |
|||
|
|||
ThreadQueue.Enqueue(task); |
|||
tasks.Add(task); |
|||
maxBlockSize = Math.Min(MaxBlockSize, maxBlockSize * ScalingFactor); |
|||
} |
|||
|
|||
if (tasks.Count > 0) |
|||
{ |
|||
WaitForTasksToComplete(tasks.ToArray()); |
|||
CollectExceptions(tasks); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Executes a for each operation on an IEnumerable{TSource in which iterations may run in parallel.
|
|||
/// </summary>
|
|||
/// <typeparam name="TSource">The type of the data in the source.</typeparam>
|
|||
/// <typeparam name="TLocal">The type of the thread-local data.</typeparam>
|
|||
/// <param name="source">An enumerable data source.</param>
|
|||
/// <param name="localInit">The function delegate that returns the initial state of the local data for each thread.</param>
|
|||
/// <param name="body">The delegate that is invoked once per iteration.</param>
|
|||
/// <param name="localFinally">The delegate that performs a final action on the local state of each thread.</param>
|
|||
public static void ForEach<TSource, TLocal>(IEnumerable<TSource> source, Func<TLocal> localInit, Func<TSource, TLocal, TLocal> body, Action<TLocal> localFinally) |
|||
{ |
|||
if (body == null) |
|||
{ |
|||
throw new ArgumentNullException("body"); |
|||
} |
|||
|
|||
// fast forward execution in case parallelization is disabled
|
|||
if (Control.DisableParallelization |
|||
|| ThreadQueue.ThreadCount <= 1 |
|||
|| ThreadQueue.IsInWorkerThread) |
|||
{ |
|||
var localResult = localInit(); |
|||
foreach (var item in source) |
|||
{ |
|||
localResult = body(item, localResult); |
|||
} |
|||
|
|||
localFinally(localResult); |
|||
return; |
|||
} |
|||
|
|||
// source is a IList, call For instead.
|
|||
if (source is IList<TSource>) |
|||
{ |
|||
var list = (IList<TSource>)source; |
|||
For(0, list.Count, localInit, (i, local) => body(list[i], local), localFinally); |
|||
return; |
|||
} |
|||
|
|||
var maxBlockSize = IntialBlockSize; |
|||
var tasks = new List<Task<TLocal>>(); |
|||
|
|||
var enumerator = source.GetEnumerator(); |
|||
while (enumerator.MoveNext()) |
|||
{ |
|||
var pos = 0; |
|||
var list = new TSource[maxBlockSize]; |
|||
list[pos++] = enumerator.Current; |
|||
|
|||
var count = 1; |
|||
while (count < maxBlockSize && enumerator.MoveNext()) |
|||
{ |
|||
list[pos++] = enumerator.Current; |
|||
count++; |
|||
} |
|||
|
|||
var task = new Task<TLocal>( |
|||
localData => |
|||
{ |
|||
var localresult = localData; |
|||
for (var i = 0; i < pos; i++) |
|||
{ |
|||
localresult = body(list[i], (TLocal)localresult); |
|||
} |
|||
|
|||
return (TLocal)localresult; |
|||
}, |
|||
localInit()); |
|||
|
|||
ThreadQueue.Enqueue(task); |
|||
tasks.Add(task); |
|||
maxBlockSize = Math.Min(MaxBlockSize, maxBlockSize * ScalingFactor); |
|||
} |
|||
|
|||
if (tasks.Count <= 0) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var taskArray = tasks.ToArray(); |
|||
WaitForTasksToComplete(taskArray); |
|||
|
|||
for (var i = 0; i < taskArray.Length; i++) |
|||
{ |
|||
localFinally(tasks[i].Result); |
|||
} |
|||
|
|||
CollectExceptions(taskArray); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Executes each of the provided actions inside a discrete, asynchronous task.
|
|||
/// </summary>
|
|||
/// <param name="actions">An array of actions to execute.</param>
|
|||
/// <exception cref="ArgumentNullException">The <paramref name="actions"/> argument is null.</exception>
|
|||
/// <exception cref="ArgumentException">The actions array contains a null element.</exception>
|
|||
/// <exception cref="AggregateException">An action threw an exception.</exception>
|
|||
public static void Run(params Action[] actions) |
|||
{ |
|||
if (actions == null) |
|||
{ |
|||
throw new ArgumentNullException("actions"); |
|||
} |
|||
|
|||
// fast forward execution if it's only one or none items
|
|||
if (actions.Length <= 1) |
|||
{ |
|||
if (actions.Length == 1) |
|||
{ |
|||
actions[0](); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
// fast forward execution in case parallelization is disabled
|
|||
if (Control.DisableParallelization |
|||
|| ThreadQueue.ThreadCount <= 1 |
|||
|| ThreadQueue.IsInWorkerThread) |
|||
{ |
|||
for (var i = 0; i < actions.Length; i++) |
|||
{ |
|||
actions[i](); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
Invoke(actions); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Executes each of the provided actions inside a discrete, asynchronous task.
|
|||
/// </summary>
|
|||
/// <param name="actions">An array of actions to execute.</param>
|
|||
/// <exception cref="ArgumentException">The actions array contains a null element.</exception>
|
|||
/// <exception cref="AggregateException">An action threw an exception.</exception>
|
|||
internal static void Invoke(params Action[] actions) |
|||
{ |
|||
// create a job for each action
|
|||
var tasks = new Task[actions.Length]; |
|||
for (var i = 0; i < tasks.Length; i++) |
|||
{ |
|||
Action action = actions[i]; |
|||
if (action == null) |
|||
{ |
|||
throw new ArgumentException(String.Format(Resources.ArgumentItemNull, "actions"), "actions"); |
|||
} |
|||
|
|||
tasks[i] = new Task(action); |
|||
} |
|||
|
|||
// run the jobs
|
|||
ThreadQueue.Enqueue(tasks); |
|||
|
|||
WaitForTasksToComplete(tasks); |
|||
|
|||
CollectExceptions(tasks); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Waits for tasks to complete.
|
|||
/// </summary>
|
|||
/// <param name="tasks">The tasks.</param>
|
|||
private static void WaitForTasksToComplete(Task[] tasks) |
|||
{ |
|||
foreach (var task in tasks) |
|||
{ |
|||
task.Wait(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Collects the exceptions and dispose tasks.
|
|||
/// </summary>
|
|||
/// <param name="tasks">The tasks.</param>
|
|||
private static void CollectExceptions(IEnumerable<Task> tasks) |
|||
{ |
|||
// collect all thrown exceptions and dispose the jobs
|
|||
var exceptions = new List<Exception>(); |
|||
foreach (var task in tasks) |
|||
{ |
|||
if (task.IsFaulted) |
|||
{ |
|||
exceptions.Add(task.Exception); |
|||
} |
|||
} |
|||
|
|||
// throw the aggregated exceptions, if any
|
|||
if (exceptions.Count > 0) |
|||
{ |
|||
throw new AggregateException(exceptions); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,128 @@ |
|||
// <copyright file="Task.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://mathnet.opensourcedotnet.info
|
|||
//
|
|||
// Copyright (c) 2009 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.Threading |
|||
{ |
|||
using System; |
|||
using System.Threading; |
|||
|
|||
/// <summary>
|
|||
/// Internal Parallel Task Handle.
|
|||
/// </summary>
|
|||
internal class Task |
|||
{ |
|||
/// <summary>
|
|||
/// Delegate to the task's action.
|
|||
/// </summary>
|
|||
private readonly System.Action _body; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the Task class.
|
|||
/// </summary>
|
|||
/// <param name="body">Delegate to the task's action.</param>
|
|||
public Task(Action body) |
|||
{ |
|||
if (body == null) |
|||
{ |
|||
throw new ArgumentNullException("body"); |
|||
} |
|||
|
|||
_body = body; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Task"/> class.
|
|||
/// </summary>
|
|||
protected Task() |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether the task completed due to an unhandled exception.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// <c>true</c> if this task completed due to an unhandled exception; otherwise, <c>false</c>.
|
|||
/// </value>
|
|||
public bool IsFaulted |
|||
{ |
|||
get { return Exception != null; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether this task has completed.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// <c>true</c> if this task has completed; otherwise, <c>false</c>.
|
|||
/// </value>
|
|||
public bool IsCompleted |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the exception thrown by the task, if any.
|
|||
/// </summary>
|
|||
public Exception Exception { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Run the task.
|
|||
/// </summary>
|
|||
public void Compute() |
|||
{ |
|||
try |
|||
{ |
|||
DoCompute(); |
|||
IsCompleted = true; |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
Exception = e; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Runs the actual task.
|
|||
/// </summary>
|
|||
protected virtual void DoCompute() |
|||
{ |
|||
_body(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Waits for the task to complete execution.
|
|||
/// </summary>
|
|||
public void Wait() |
|||
{ |
|||
while (!IsCompleted && !IsFaulted) |
|||
{ |
|||
Thread.Sleep(0); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,79 @@ |
|||
// <copyright file="TaskOfT.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://mathnet.opensourcedotnet.info
|
|||
//
|
|||
// Copyright (c) 2009 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.Threading |
|||
{ |
|||
using System; |
|||
|
|||
/// <summary>
|
|||
/// Internal Generic Parallel Task Handle.
|
|||
/// </summary>
|
|||
/// <typeparam name="TResult">The type of the result.</typeparam>
|
|||
internal class Task<TResult> : Task |
|||
{ |
|||
/// <summary>
|
|||
/// Delegate to the task's action.
|
|||
/// </summary>
|
|||
private readonly Func<object, TResult> _body; |
|||
|
|||
/// <summary>
|
|||
/// Variable used to hold state information between iterations.
|
|||
/// </summary>
|
|||
private readonly object _state; |
|||
|
|||
/// <summary>
|
|||
/// Gets the result of the task.
|
|||
/// </summary>
|
|||
/// <value>The result of the task.</value>
|
|||
public TResult Result { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the Task class.
|
|||
/// </summary>
|
|||
/// <param name="body">Delegate to the task's action.</param>
|
|||
/// <param name="state">An object representing data to be used by the action.</param>
|
|||
public Task(Func<object, TResult> body, object state) |
|||
{ |
|||
if (body == null) |
|||
{ |
|||
throw new ArgumentNullException("body"); |
|||
} |
|||
|
|||
_state = state; |
|||
_body = body; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Runs the actual task.
|
|||
/// </summary>
|
|||
protected override void DoCompute() |
|||
{ |
|||
Result = _body(_state); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,258 @@ |
|||
// <copyright file="ThreadQueue.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://mathnet.opensourcedotnet.info
|
|||
//
|
|||
// Copyright (c) 2009 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
namespace MathNet.Numerics.Threading |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
|
|||
/// <summary>
|
|||
/// Internal Parallel Thread Queue.
|
|||
/// </summary>
|
|||
internal static class ThreadQueue |
|||
{ |
|||
/// <summary>
|
|||
/// Sync Object for the thread queue state.
|
|||
/// </summary>
|
|||
private static readonly object _stateSync = new object(); |
|||
|
|||
/// <summary>
|
|||
/// Sync Object for queue access (to be sure it's used by us only).
|
|||
/// </summary>
|
|||
private static readonly object _queueSync = new object(); |
|||
|
|||
/// <summary>
|
|||
/// Queue holding the pending jobs.
|
|||
/// </summary>
|
|||
private static readonly Queue<Task> _queue = new Queue<Task>(); |
|||
|
|||
/// <summary>
|
|||
/// Running flag, used to signal worker threads to stop cleanly.
|
|||
/// </summary>
|
|||
private static bool _running = true; |
|||
|
|||
/// <summary>
|
|||
/// Worker threads
|
|||
/// </summary>
|
|||
private static Thread[] _threads; |
|||
|
|||
/// <summary>
|
|||
/// Gets the number of worker threads.
|
|||
/// </summary>
|
|||
internal static int ThreadCount { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Indicating whether the current thread is a parallelized worker thread.
|
|||
/// </summary>
|
|||
[ThreadStatic] |
|||
private static bool _isInWorkerThread; |
|||
|
|||
/// <summary>
|
|||
/// Initializes static members of the ThreadQueue class.
|
|||
/// </summary>
|
|||
static ThreadQueue() |
|||
{ |
|||
Start(1); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether the current thread is a parallelized worker thread.
|
|||
/// </summary>
|
|||
public static bool IsInWorkerThread |
|||
{ |
|||
get { return _isInWorkerThread; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Add a job to the queue.
|
|||
/// </summary>
|
|||
/// <param name="task">The job to run.</param>
|
|||
public static void Enqueue(Task task) |
|||
{ |
|||
if (!_running) |
|||
{ |
|||
Start(); |
|||
} |
|||
|
|||
lock (_queueSync) |
|||
{ |
|||
_queue.Enqueue(task); |
|||
Monitor.Pulse(_queueSync); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Add a set of jobs to the queue.
|
|||
/// </summary>
|
|||
/// <param name="tasks">The jobs to run.</param>
|
|||
public static void Enqueue(IList<Task> tasks) |
|||
{ |
|||
if (!_running) |
|||
{ |
|||
Start(); |
|||
} |
|||
|
|||
lock (_queueSync) |
|||
{ |
|||
foreach (var task in tasks) |
|||
{ |
|||
_queue.Enqueue(task); |
|||
} |
|||
|
|||
Monitor.PulseAll(_queueSync); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Worker Thread Program
|
|||
/// </summary>
|
|||
private static void WorkerThreadStart() |
|||
{ |
|||
_isInWorkerThread = true; |
|||
|
|||
while (_running) |
|||
{ |
|||
// Get the job...
|
|||
Task task = null; |
|||
|
|||
lock (_queueSync) |
|||
{ |
|||
// Check whether we should shut down
|
|||
if (!_running) |
|||
{ |
|||
break; |
|||
} |
|||
|
|||
if (_queue.Count > 0) |
|||
{ |
|||
task = _queue.Dequeue(); |
|||
} |
|||
else |
|||
{ |
|||
Monitor.Wait(_queueSync); |
|||
} |
|||
} |
|||
|
|||
if (task == null) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
// ...and run it
|
|||
task.Compute(); |
|||
} |
|||
|
|||
_isInWorkerThread = false; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Start or restart the queue with the specified number of worker threads.
|
|||
/// </summary>
|
|||
/// <param name="numberOfThreads">Number of worker threads.</param>
|
|||
public static void Start(int numberOfThreads) |
|||
{ |
|||
lock (_stateSync) |
|||
{ |
|||
// instead of throwing an out of range exception, simply normalize
|
|||
numberOfThreads = Math.Max(1, Math.Min(1024, numberOfThreads)); |
|||
|
|||
if (_threads != null) |
|||
{ |
|||
if (_threads.Length == numberOfThreads) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
Shutdown(); |
|||
} |
|||
|
|||
ThreadCount = numberOfThreads; |
|||
Start(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Start the thread queue, if it is not already running.
|
|||
/// </summary>
|
|||
public static void Start() |
|||
{ |
|||
lock (_stateSync) |
|||
{ |
|||
if (_threads != null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
_running = true; |
|||
|
|||
_threads = new Thread[ThreadCount]; |
|||
|
|||
for (var i = 0; i < _threads.Length; i++) |
|||
{ |
|||
_threads[i] = new Thread(WorkerThreadStart) |
|||
{ |
|||
IsBackground = true |
|||
}; |
|||
|
|||
_threads[i].Start(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Stop the thread queue, if it is running.
|
|||
/// </summary>
|
|||
public static void Shutdown() |
|||
{ |
|||
// try to stop the worker threads cleanly
|
|||
lock (_stateSync) |
|||
{ |
|||
if (_threads == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
_running = false; |
|||
|
|||
lock (_queueSync) |
|||
{ |
|||
Monitor.PulseAll(_queueSync); |
|||
} |
|||
|
|||
// wait until all threads have stopped
|
|||
foreach (var thread in _threads) |
|||
{ |
|||
thread.Join(); |
|||
} |
|||
|
|||
_threads = null; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue