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