Параллельные исключения перехватываются

почему-то кажется, что мои исключения перехватываются методом, в котором они выполняются. Вот код для вызова метода. Как видите, я создаю токен отмены с тайм-аутом. Я регистрирую метод для вызова при срабатывании токена отмены, а затем запускаю новую задачу. Токен отмены работает нормально. Как и зарегистрированный метод.

var cancellationToken = new CancellationTokenSource(subscriber.TimeToExpire).Token;
cancellationToken.Register(() =>
{
    subscriber.Abort();
});
var task = Task<bool>.Factory.StartNew(() =>
{                  
 subscriber.RunAsync((T)messagePacket.Body, cancellationToken);
 return true;
})
.ContinueWith(anticedant => 
{
    if (anticedant.IsCanceled)
    {
        Counter.Increment(12);
        Trace.WriteLine("Request was canceled");
    }

    if (anticedant.IsFaulted)
    {
        Counter.Increment(13);
        Trace.WriteLine("Request was canceled");
    }

    if (anticedant.IsCompleted)
    {
        Counter.Increment(14);
    }

Следующий фрагмент кода — это метод, который, кажется, не только выдает исключение (ожидаемое поведение, но и перехватывает исключение.

public async override Task<bool> ProcessAsync(Message input, CancellationToken cancellationToken)
{
    Random r = new Random();
    Thread.Sleep(r.Next(90, 110));
    cancellationToken.ThrowIfCancellationRequested();
    return await DoSomethingAsync(input);
}

Исключение вызывается токеном отмены, но, согласно intellitrace, оно перехватывается в конце метода. Я пробовал несколько разных вариантов, включая создание собственного исключения, но независимо от того, что функция continuewith всегда выполняет IsComleted или выполняет код завершения. Любые идеи о том, что я делаю неправильно? Спасибо


person Noel    schedule 25.07.2012    source источник
comment
Трудно понять, что именно происходит с вашим кодом, когда мы не видим всего этого. Не могли бы вы создать небольшой, но полный образец, показывающий вашу проблему? Кроме того, в чем разница между RunAsync() и ProcessAsync()?   -  person svick    schedule 25.07.2012
comment
Извините, я пропустил RunAsync/ProcessAsync при извлечении моего примера кода. Выполнение вызовов Process с помощью нескольких синхронных методов до и после   -  person Noel    schedule 25.07.2012


Ответы (1)


Я предполагаю, что RunAsync совпадает с ProcessAsync.

Исключение вызывается токеном отмены, но, согласно intellitrace, оно перехватывается в конце метода.

Ага. Любой метод async будет перехватывать собственные исключения и помещать их в возвращенный Task. Это по дизайну.

независимо от того, что функция continuewith всегда выполняет код IsComleted или выполняется до завершения.

Что ж, давайте еще раз взглянем на код:

var task = Task<bool>.Factory.StartNew(() =>
{                  
  subscriber.RunAsync((T)messagePacket.Body, cancellationToken);
  return true;
})
.ContinueWith(

Рассмотрим лямбду, переданную StartNew: она вызывает RunAsync, игнорирует возвращаемую Task, а затем возвращает true (успешно). Таким образом, Task, возвращаемый StartNew, всегда будет успешно возвращаться. Вот почему ContinueWith всегда выполняется для успешно завершенной задачи.

Что вы действительно хотите, так это await Task, возвращенное RunAsync. Итак, что-то вроде этого:

var task = Task.Run(async () =>
{                  
  await subscriber.RunAsync((T)messagePacket.Body, cancellationToken);
  return true;
})
.ContinueWith(

Вы по-прежнему игнорируете возвращаемое значение RunAsync (возвращаемое им bool игнорируется), но вы не игнорируете само Task (включая информацию об отмене/исключении).

P.S. Я предполагаю, что вы нам не показываете много кода; использование StartNew/Run для запуска асинхронной работы и возврата значения обходится очень дорого.

П.П.С. Используйте await Task.Delay вместо Thread.Sleep.

person Stephen Cleary    schedule 25.07.2012
comment
Спасибо, это именно то, чего мне не хватало. - person Noel; 25.07.2012