10

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?

3
  • 4
    Unfortunately, you're not capturing enough in your log. An AggregateException may contain any number of other exceptions, and it's those exceptions that are more likely to contain useful information to assist with diagnosing the issue. Commented Apr 4, 2013 at 6:37
  • Also "The thread ends after about 10 minutes and every 2 hours ... in case the thread was still running." - It sounds like you're already glossing over an issue that you should be investigating - it normally takes 10 minutes, but it's sometimes still running after 2 hours... Commented Apr 4, 2013 at 7:03
  • there's a client program which can cancel the thread at any time and restart it so the cancellation is definitely needed Commented Apr 4, 2013 at 13:55

1 Answer 1

19

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:

  • don't let your tasks throw,
  • or make sure that you're there to check the status of the task later

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.