Reputation: 9733
Consider the following code which uses basic Task library functionality with a CancellationTokenSource. It starts up a thread which fills a Dictionary with prices and reads the data from an SQL server db. The thread ends after about 10 minutes and every 2 hours it is fired up again, calling Cancel first in case the thread was still running.
private CancellationTokenSource mTokenSource = new CancellationTokenSource();
internal Prices(Dictionary<string, Dealer> dealers)
{
mDealers = dealers;
mTask = Task.Factory.StartNew
(() => ReadPrices(mTokenSource.Token), mTokenSource.Token);
}
internal void Cancel()
{
mTokenSource.Cancel();
}
private void ReadPrices(CancellationToken ct)
{
using (SqlConnection connection =
new SqlConnection(ConfigurationManager.AppSettings["DB"]))
{
connection.Open();
var dealerIds = from dealer in mDealers.Values
where dealer.Id != null
select dealer.Id;
foreach (var dealerId in dealerIds)
{
if (!ct.IsCancellationRequested)
{
FillPrices(connection);
}
else
break;
}
}
}
Now at some point the application crashes with the following exception in the event log.
Application: Engine.exe Framework Version: v4.0.30319 Description: The process was terminated due to an unhandled exception. Exception Info: System.AggregateException Stack: at System.Threading.Tasks.TaskExceptionHolder.Finalize()
It must have to do with the code here because the Tasks library isn't used anywhere else but I cant figure out what is wrong with the code. Does anyone have an idea what could be wrong here?
Upvotes: 10
Views: 6491
Reputation: 1062502
Tasks like to feel listened to. It sounds like something isn't happy. You do get a "last chance" to hear it out, though:
TaskScheduler.UnobservedTaskException += (sender, args) =>
{
foreach (var ex in args.Exception.InnerExceptions)
{
Log(ex);
}
args.SetObserved();
};
Note this isn't intended as the fix - it is intended to let you see what Task
is exploding and with what error. The SetObserved()
will prevent it killing your application. But the fix here is ideally:
It is quite possibly not happy with your cancellation detection. IIRC the preferred way to do that would be:
foreach(...) {
if(ct.IsCancellationRequested) {
// any cleanup etc
ct.ThrowIfCancellationRequested();
}
...
}
or more simply if no cleanup is needed, just:
foreach(...) {
ct.ThrowIfCancellationRequested();
...
}
Equally, it could just be a data access exception. There are any number of exceptions that can happen when talking to a database. Timeout, deadlock, cannot-connect, etc.
Upvotes: 19