I am developing a proof of concept app that factors a list of numbers using tasks and a semaphore, currently I have a List of Tasks,List<Task>
, that take a FactorNumberClass
and then calculate the factors of the specific number within the FactorNumberClass
this currently works correctly. With each Task T, I have a ContinueWith
task that updates the progress of the total numbers factored, the average time for factoring, and updates a progress bar with the value of (Numbers successfully factored)/(Total numbers to be factored). When factoring these Tasks
enter a SemaphoreSlim.Wait(cancelToken)
that limits the current factoring to 5 active Tasks
. Lastly I have a ContinueWhenAll
that logs when all tasks have completed. Assuming no cancellation, this all works as I intend.
The problem arises when I attempt to cancel the Tasks, I can not detect whether or not the task has been cancelled and therefore can not make an accurate determination on whether the number has been successfully factored or if it was cancelled. How can I detect whether or not the parent task was cancelled or ran to completion?
Cancellation Token Definition:
public static CancellationTokenSource tokenSource = new CancellationTokenSource();
public static CancellationToken ct = tokenSource.Token;
Factor Class Code:
public class FactorNumberClass
{
public FactorNumberClass()
{
}
public FactorNumberClass(int num, int threadnum)
{
this.number = num;
this.threadNumber = threadnum;
}
public List<int> factors = new List<int>();
public int number;
public int max;
public int threadNumber;
}
Factoring Method:
public void Factor(FactorNumberClass F, CancellationToken token)
{
LogtoStatusText("Thread: " + F.threadNumber + " Trying to enter semaphore");
try
{
ASemaphore.Wait(ct);
F.max = (int)Math.Sqrt(F.number); //round down
for (int factor = 1; factor <= F.max; ++factor)
{ //test from 1 to the square root, or the int below it, inclusive.
if (F.number % factor == 0)
{
F.factors.Add(factor);
if (factor != F.number / factor)
{
F.factors.Add(F.number / factor);
}
}
}
F.factors.Sort();
Thread.Sleep(F.number * 300);
LogtoStatusText("Task: " + F.threadNumber + " Completed - Factors: " + string.Join(",", F.factors.ToArray()));
LogtoStatusText("Thread: " + F.threadNumber + " Releases semaphore with previous count: " + ASemaphore.Release());
}
catch (OperationCanceledException ex)
{
LogtoStatusText("Thread: " + F.threadNumber + " Cancelled.");
}
finally
{
}
}
Method that starts the processing:
public void btnStart_Click(object sender, RoutedEventArgs e)
{
Task T;
List<Task> TaskList = new List<Task>();
LogtoStatusText("**** Begin creating tasks *****");
s1.Start();
AProject.FactorClassList.ForEach((f) =>
{
T = new Task(((x) => { OnUIThread(() => { RunningTasks++; }); Factor(f, ct); }), ct);
T.ContinueWith((y) =>
{
if (y.IsCompleted)
{
AProject.TotalProcessedAccounts++;
AProject.AverageProcessTime = (((Double)AProject.TotalProcessedAccounts / s1.ElapsedMilliseconds) * 1000);
}
OnUIThread(() => { RunningTasks--; });
OnUIThread(() => { UpdateCounts(AProject); });
});
TaskList.Add(T);
});
try
{
Task.Factory.ContinueWhenAll(TaskList.ToArray(), (z) => { LogtoStatusText("**** Completed all Tasks *****"); OnUIThread(() => { UpdateCounts(AProject); }); });
}
catch (AggregateException a)
{
// For demonstration purposes, show the OCE message.
foreach (var v in a.InnerExceptions)
LogtoStatusText("msg: " + v.Message);
}
LogtoStatusText("**** All tasks have been initialized, begin processing *****");
TaskList.ForEach(t => t.Start());
}