diff --git a/Source/ParallelBackgroundWorker.Test.cs b/Source/ParallelBackgroundWorker.Test.cs index f1c56a7..58ac729 100644 --- a/Source/ParallelBackgroundWorker.Test.cs +++ b/Source/ParallelBackgroundWorker.Test.cs @@ -95,7 +95,9 @@ namespace Nuclex.Support { this.WasCancelled = cancellationToken.IsCancellationRequested; if(this.Tasks != null) { - this.Tasks.Add(task); + lock(this.Tasks) { + this.Tasks.Add(task); + } } } @@ -236,7 +238,9 @@ namespace Nuclex.Support { waitEvent.Set(); testWorker.Join(); - CollectionAssert.AreEquivalent(tasks, testWorker.Tasks); + lock(testWorker.Tasks) { + CollectionAssert.AreEquivalent(tasks, testWorker.Tasks); + } } } // disposes waitEvent } diff --git a/Source/ParallelBackgroundWorker.cs b/Source/ParallelBackgroundWorker.cs index 3572b69..0fc9697 100644 --- a/Source/ParallelBackgroundWorker.cs +++ b/Source/ParallelBackgroundWorker.cs @@ -94,9 +94,11 @@ namespace Nuclex.Support { this.threadTerminatedEvent.Dispose(); this.threadTerminatedEvent = null; } - if(this.cancellationTokenSource != null) { - this.cancellationTokenSource.Dispose(); - this.cancellationTokenSource = null; + lock(this.queueSynchronizationRoot) { + if(this.cancellationTokenSource != null) { + this.cancellationTokenSource.Dispose(); + this.cancellationTokenSource = null; + } } } @@ -132,11 +134,21 @@ namespace Nuclex.Support { } /// Cancels all tasks that are currently executing + /// + /// It is valid to call this method after Dispose() + /// public void CancelRunningTasks() { - this.cancellationTokenSource.Cancel(); + lock(this.queueSynchronizationRoot) { + if(this.cancellationTokenSource != null) { + this.cancellationTokenSource.Cancel(); + } + } } /// Cancels all queued tasks waiting to be executed + /// + /// It is valid to call this method after Dispose() + /// public void CancelPendingTasks() { lock(this.queueSynchronizationRoot) { this.tasks.Clear(); @@ -166,6 +178,15 @@ namespace Nuclex.Support { /// True if all tasks have been processed, false if the timeout was reached /// public bool Wait(int timeoutMilliseconds) { + + // Wait until the task queue has become empty + while(queuedTaskCount > 0) { + if(this.threadTerminatedEvent.WaitOne(timeoutMilliseconds) == false) { + return false; + } + } + + // Now wait until all running tasks have finished while(Thread.VolatileRead(ref this.runningThreadCount) > 0) { if(this.threadTerminatedEvent.WaitOne(timeoutMilliseconds) == false) { return false; @@ -173,6 +194,7 @@ namespace Nuclex.Support { } return true; + } /// Called in a thread to execute a single task @@ -219,6 +241,15 @@ namespace Nuclex.Support { } } + /// Number of task still waiting to be executed + private int queuedTaskCount { + get { + lock(this.queueSynchronizationRoot) { + return this.tasks.Count; + } + } + } + /// /// Name that will be assigned to the worker threads while they're processing tasks /// for the parallel background worker