11 changed files with 724 additions and 0 deletions
@ -0,0 +1,146 @@ |
|||||
|
// <copyright file="ParallelTest.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.UnitTests.ThreadingTests |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Threading; |
||||
|
using MbUnit.Framework; |
||||
|
using Threading; |
||||
|
|
||||
|
[TestFixture] |
||||
|
public class ParallelTest |
||||
|
{ |
||||
|
[Test, ApartmentState(ApartmentState.MTA)] |
||||
|
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 101)] |
||||
|
public void ParallelForInvokesEveryItemOnceMTAOnePerCore(int count) |
||||
|
{ |
||||
|
var items = new int[count]; |
||||
|
|
||||
|
// ensure One-Per-Core
|
||||
|
ThreadQueue.Start(Environment.ProcessorCount); |
||||
|
|
||||
|
Parallel.For(0, count, i => items[i]++); |
||||
|
Parallel.For(0, count, i => items[i] += 1000); |
||||
|
|
||||
|
for (int i = 0; i < items.Length; i++) |
||||
|
{ |
||||
|
Assert.AreEqual(1001, items[i], i.ToString()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Test, ApartmentState(ApartmentState.STA)] |
||||
|
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 101)] |
||||
|
public void ParallelForInvokesEveryItemOnceSTAOnePerCore(int count) |
||||
|
{ |
||||
|
var items = new int[count]; |
||||
|
|
||||
|
// ensure One-Per-Core
|
||||
|
ThreadQueue.Start(Environment.ProcessorCount); |
||||
|
|
||||
|
Parallel.For(0, count, i => items[i]++); |
||||
|
Parallel.For(0, count, i => items[i] += 1000); |
||||
|
|
||||
|
for (int i = 0; i < items.Length; i++) |
||||
|
{ |
||||
|
Assert.AreEqual(1001, items[i], i.ToString()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Test, ApartmentState(ApartmentState.MTA)] |
||||
|
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 101)] |
||||
|
public void ParallelForInvokesEveryItemOnceMTATwoPerCore(int count) |
||||
|
{ |
||||
|
var items = new int[count]; |
||||
|
|
||||
|
// ensure Two-Per-Core
|
||||
|
ThreadQueue.Start(2 * Environment.ProcessorCount); |
||||
|
|
||||
|
Parallel.For(0, count, i => items[i]++); |
||||
|
Parallel.For(0, count, i => items[i] += 1000); |
||||
|
|
||||
|
for (int i = 0; i < items.Length; i++) |
||||
|
{ |
||||
|
Assert.AreEqual(1001, items[i], i.ToString()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Test, ApartmentState(ApartmentState.STA)] |
||||
|
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 101)] |
||||
|
public void ParallelForInvokesEveryItemOnceSTATwoPerCore(int count) |
||||
|
{ |
||||
|
var items = new int[count]; |
||||
|
|
||||
|
// ensure Two-Per-Core
|
||||
|
ThreadQueue.Start(2 * Environment.ProcessorCount); |
||||
|
|
||||
|
Parallel.For(0, count, i => items[i]++); |
||||
|
Parallel.For(0, count, i => items[i] += 1000); |
||||
|
|
||||
|
for (int i = 0; i < items.Length; i++) |
||||
|
{ |
||||
|
Assert.AreEqual(1001, items[i], i.ToString()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Test, ApartmentState(ApartmentState.MTA)] |
||||
|
public void DoesNotGetConfusedByMultipleStartShutdown() |
||||
|
{ |
||||
|
ThreadQueue.Shutdown(); |
||||
|
ThreadQueue.Shutdown(); |
||||
|
|
||||
|
ThreadQueue.Start(2); |
||||
|
Assert.AreEqual(2, ThreadQueue.ThreadCount); |
||||
|
|
||||
|
ThreadQueue.Start(2); |
||||
|
Assert.AreEqual(2, ThreadQueue.ThreadCount); |
||||
|
|
||||
|
ThreadQueue.Start(4); |
||||
|
Assert.AreEqual(4, ThreadQueue.ThreadCount); |
||||
|
|
||||
|
ThreadQueue.Shutdown(); |
||||
|
ThreadQueue.Start(); |
||||
|
Assert.AreEqual(4, ThreadQueue.ThreadCount); |
||||
|
|
||||
|
ThreadQueue.Start(2); |
||||
|
Assert.AreEqual(2, ThreadQueue.ThreadCount); |
||||
|
|
||||
|
var items = new int[50]; |
||||
|
|
||||
|
Parallel.For(0, items.Length, i => items[i]++); |
||||
|
Parallel.For(0, items.Length, i => items[i] += 1000); |
||||
|
|
||||
|
ThreadQueue.Shutdown(); |
||||
|
|
||||
|
for(int i = 0; i < items.Length; i++) |
||||
|
{ |
||||
|
Assert.AreEqual(1001, items[i], i.ToString()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,66 @@ |
|||||
|
// <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) |
||||
|
{ |
||||
|
_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>(_exceptions); } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,158 @@ |
|||||
|
// <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 System.Threading; |
||||
|
using Properties; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides support for parallel loops.
|
||||
|
/// </summary>
|
||||
|
internal static class Parallel |
||||
|
{ |
||||
|
/// <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>
|
||||
|
internal static void For(int fromInclusive, int toExclusive, Action<int> body) |
||||
|
{ |
||||
|
if (body == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException("body"); |
||||
|
} |
||||
|
|
||||
|
var actions = new Action[ThreadQueue.ThreadCount]; |
||||
|
var count = toExclusive - fromInclusive; |
||||
|
var size = count / actions.Length; |
||||
|
|
||||
|
if (count < 1) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// 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 (var j = start; j < stop; j++) |
||||
|
{ |
||||
|
body(j); |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
// add another set for last worker thread
|
||||
|
actions[actions.Length - 1] = |
||||
|
() => |
||||
|
{ |
||||
|
for (var i = fromInclusive + ((actions.Length - 1) * size); i < toExclusive; i++) |
||||
|
{ |
||||
|
body(i); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
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="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>
|
||||
|
internal static void Invoke(params Action[] actions) |
||||
|
{ |
||||
|
if (actions == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException("actions"); |
||||
|
} |
||||
|
|
||||
|
// create a job for each action
|
||||
|
var tasks = new Task[actions.Length]; |
||||
|
for (int 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); |
||||
|
|
||||
|
// wait until all jobs have completed
|
||||
|
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) |
||||
|
{ |
||||
|
//not sure if this the best approach for STA
|
||||
|
for (int i = 0; i < tasks.Length; i++) |
||||
|
{ |
||||
|
tasks[i].WaitOne(); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
WaitHandle.WaitAll(tasks); |
||||
|
} |
||||
|
|
||||
|
// collect all thrown exceptions and dispose the jobs
|
||||
|
var exceptions = new List<Exception>(); |
||||
|
foreach (var task in tasks) |
||||
|
{ |
||||
|
if (task.ThrewException) |
||||
|
{ |
||||
|
exceptions.Add(task.Exception); |
||||
|
} |
||||
|
|
||||
|
//this calls dispose
|
||||
|
task.Close(); |
||||
|
} |
||||
|
|
||||
|
// throw the aggregated exceptions, if any
|
||||
|
if (exceptions.Count > 0) |
||||
|
{ |
||||
|
throw new AggregateException(exceptions); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,87 @@ |
|||||
|
// <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 : EventWaitHandle |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Delegate to the task's action.
|
||||
|
/// </summary>
|
||||
|
private readonly Action _body; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the Task class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="body">Delegate to the task's action.</param>
|
||||
|
internal Task(Action body) |
||||
|
: base(false, EventResetMode.ManualReset) |
||||
|
{ |
||||
|
if (body == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException("body"); |
||||
|
} |
||||
|
|
||||
|
_body = body; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether the task has thrown one or more exceptions while executing.
|
||||
|
/// </summary>
|
||||
|
internal bool ThrewException |
||||
|
{ |
||||
|
get { return Exception != null; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the exception thrown by the task, if any.
|
||||
|
/// </summary>
|
||||
|
internal Exception Exception { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Run the task.
|
||||
|
/// </summary>
|
||||
|
internal void Compute() |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
_body(); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
Exception = e; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,235 @@ |
|||||
|
// <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>
|
||||
|
/// Maximum number of jobs that can be in the queue at the same time.
|
||||
|
/// </summary>
|
||||
|
private const int _maximumQueueLength = 1024; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Counting Semaphore to make the worker thread wait for jobs
|
||||
|
/// </summary>
|
||||
|
private static Semaphore _tasksAvailableSemaphore; |
||||
|
|
||||
|
/// <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>
|
||||
|
/// Number of worked threads.
|
||||
|
/// </summary>
|
||||
|
internal static int ThreadCount { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Static Constructor
|
||||
|
/// </summary>
|
||||
|
static ThreadQueue() |
||||
|
{ |
||||
|
// TODO: Control.ThreadCount instead of Environment.ProcessorCount
|
||||
|
Start(Environment.ProcessorCount); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Add a job to the queue.
|
||||
|
/// </summary>
|
||||
|
/// <param name="task">The job to run.</param>
|
||||
|
internal static void Enqueue(Task task) |
||||
|
{ |
||||
|
if(!_running) |
||||
|
{ |
||||
|
Start(); |
||||
|
} |
||||
|
|
||||
|
lock(_queueSync) |
||||
|
{ |
||||
|
_queue.Enqueue(task); |
||||
|
} |
||||
|
|
||||
|
_tasksAvailableSemaphore.Release(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Add a set of jobs to the queue.
|
||||
|
/// </summary>
|
||||
|
/// <param name="tasks">The jobs to run.</param>
|
||||
|
internal static void Enqueue(IList<Task> tasks) |
||||
|
{ |
||||
|
if(!_running) |
||||
|
{ |
||||
|
Start(); |
||||
|
} |
||||
|
|
||||
|
lock(_queueSync) |
||||
|
{ |
||||
|
foreach(var task in tasks) |
||||
|
{ |
||||
|
_queue.Enqueue(task); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
_tasksAvailableSemaphore.Release(tasks.Count); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Worker Thread Program
|
||||
|
/// </summary>
|
||||
|
private static void WorkerThreadStart() |
||||
|
{ |
||||
|
while (_running) |
||||
|
{ |
||||
|
// Wait until a job is available, or we should shut down
|
||||
|
_tasksAvailableSemaphore.WaitOne(); |
||||
|
|
||||
|
// Check whether we should shut down
|
||||
|
if(!_running) |
||||
|
{ |
||||
|
_tasksAvailableSemaphore.Release(); |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
// Get the job...
|
||||
|
Task task = null; |
||||
|
lock (_queueSync) |
||||
|
{ |
||||
|
if(_queue.Count > 0) |
||||
|
{ |
||||
|
task = _queue.Dequeue(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// ...and run it
|
||||
|
if (task != null) |
||||
|
{ |
||||
|
task.Compute(); |
||||
|
task.Set(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal static void Start(int numberOfThreads) |
||||
|
{ |
||||
|
lock (_stateSync) |
||||
|
{ |
||||
|
if (_threads != null) |
||||
|
{ |
||||
|
if (_threads.Length == numberOfThreads) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
Shutdown(); |
||||
|
} |
||||
|
|
||||
|
ThreadCount = numberOfThreads; |
||||
|
Start(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal static void Start() |
||||
|
{ |
||||
|
lock (_stateSync) |
||||
|
{ |
||||
|
if (_threads != null) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
_tasksAvailableSemaphore = new Semaphore(_queue.Count, _maximumQueueLength); |
||||
|
_running = true; |
||||
|
_threads = new Thread[ThreadCount]; |
||||
|
|
||||
|
for (var i = 0; i < _threads.Length; i++) |
||||
|
{ |
||||
|
_threads[i] = new Thread(WorkerThreadStart) |
||||
|
{ |
||||
|
IsBackground = true |
||||
|
}; |
||||
|
|
||||
|
_threads[i].Start(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal static void Shutdown() |
||||
|
{ |
||||
|
lock (_stateSync) |
||||
|
{ |
||||
|
if (_threads == null) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// try to stop the worker threads cleanly
|
||||
|
_running = false; |
||||
|
_tasksAvailableSemaphore.Release(); |
||||
|
|
||||
|
// wait until all threads have stopped
|
||||
|
foreach (var thread in _threads) |
||||
|
{ |
||||
|
thread.Join(); |
||||
|
} |
||||
|
|
||||
|
_tasksAvailableSemaphore.Close(); |
||||
|
_tasksAvailableSemaphore = null; |
||||
|
_threads = null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue