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