Made the ParallelBackgroundWorker.CancelRunningTasks() and .CancelPendingTasks() methods safe to call after Dispose() (since many usage patterns become much easier with this allowed); Wait() didn't wait for queued tasks (though as long as tasks are queued, there will always be executing tasks, so this is just me being pedantic) - fixed; fixed a race condition in one of the unit tests for the ParallelBackgroundWorker
git-svn-id: file:///srv/devel/repo-conversion/nusu@293 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
parent
a2b92248b5
commit
0a7306bb58
|
@ -95,9 +95,11 @@ namespace Nuclex.Support {
|
||||||
this.WasCancelled = cancellationToken.IsCancellationRequested;
|
this.WasCancelled = cancellationToken.IsCancellationRequested;
|
||||||
|
|
||||||
if(this.Tasks != null) {
|
if(this.Tasks != null) {
|
||||||
|
lock(this.Tasks) {
|
||||||
this.Tasks.Add(task);
|
this.Tasks.Add(task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Whether the work tasks should throw exceptions</summary>
|
/// <summary>Whether the work tasks should throw exceptions</summary>
|
||||||
public bool ThrowException;
|
public bool ThrowException;
|
||||||
|
@ -236,8 +238,10 @@ namespace Nuclex.Support {
|
||||||
waitEvent.Set();
|
waitEvent.Set();
|
||||||
testWorker.Join();
|
testWorker.Join();
|
||||||
|
|
||||||
|
lock(testWorker.Tasks) {
|
||||||
CollectionAssert.AreEquivalent(tasks, testWorker.Tasks);
|
CollectionAssert.AreEquivalent(tasks, testWorker.Tasks);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} // disposes waitEvent
|
} // disposes waitEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,11 +94,13 @@ namespace Nuclex.Support {
|
||||||
this.threadTerminatedEvent.Dispose();
|
this.threadTerminatedEvent.Dispose();
|
||||||
this.threadTerminatedEvent = null;
|
this.threadTerminatedEvent = null;
|
||||||
}
|
}
|
||||||
|
lock(this.queueSynchronizationRoot) {
|
||||||
if(this.cancellationTokenSource != null) {
|
if(this.cancellationTokenSource != null) {
|
||||||
this.cancellationTokenSource.Dispose();
|
this.cancellationTokenSource.Dispose();
|
||||||
this.cancellationTokenSource = null;
|
this.cancellationTokenSource = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Adds a task for processing by the background worker threads</summary>
|
/// <summary>Adds a task for processing by the background worker threads</summary>
|
||||||
/// <param name="task">Task that will be processed in the background</param>
|
/// <param name="task">Task that will be processed in the background</param>
|
||||||
|
@ -132,11 +134,21 @@ namespace Nuclex.Support {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Cancels all tasks that are currently executing</summary>
|
/// <summary>Cancels all tasks that are currently executing</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// It is valid to call this method after Dispose()
|
||||||
|
/// </remarks>
|
||||||
public void CancelRunningTasks() {
|
public void CancelRunningTasks() {
|
||||||
|
lock(this.queueSynchronizationRoot) {
|
||||||
|
if(this.cancellationTokenSource != null) {
|
||||||
this.cancellationTokenSource.Cancel();
|
this.cancellationTokenSource.Cancel();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Cancels all queued tasks waiting to be executed</summary>
|
/// <summary>Cancels all queued tasks waiting to be executed</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// It is valid to call this method after Dispose()
|
||||||
|
/// </remarks>
|
||||||
public void CancelPendingTasks() {
|
public void CancelPendingTasks() {
|
||||||
lock(this.queueSynchronizationRoot) {
|
lock(this.queueSynchronizationRoot) {
|
||||||
this.tasks.Clear();
|
this.tasks.Clear();
|
||||||
|
@ -166,6 +178,15 @@ namespace Nuclex.Support {
|
||||||
/// True if all tasks have been processed, false if the timeout was reached
|
/// True if all tasks have been processed, false if the timeout was reached
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public bool Wait(int timeoutMilliseconds) {
|
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) {
|
while(Thread.VolatileRead(ref this.runningThreadCount) > 0) {
|
||||||
if(this.threadTerminatedEvent.WaitOne(timeoutMilliseconds) == false) {
|
if(this.threadTerminatedEvent.WaitOne(timeoutMilliseconds) == false) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -173,6 +194,7 @@ namespace Nuclex.Support {
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Called in a thread to execute a single task</summary>
|
/// <summary>Called in a thread to execute a single task</summary>
|
||||||
|
@ -219,6 +241,15 @@ namespace Nuclex.Support {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Number of task still waiting to be executed</summary>
|
||||||
|
private int queuedTaskCount {
|
||||||
|
get {
|
||||||
|
lock(this.queueSynchronizationRoot) {
|
||||||
|
return this.tasks.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Name that will be assigned to the worker threads while they're processing tasks
|
/// Name that will be assigned to the worker threads while they're processing tasks
|
||||||
/// for the parallel background worker
|
/// for the parallel background worker
|
||||||
|
|
Loading…
Reference in New Issue
Block a user