Перехват исключения, выданного в асинхронном обратном вызове - PullRequest
9 голосов
/ 05 января 2012

У меня есть метод, который принимает аргумент обратного вызова для выполнения асинхронно, но блок catch, похоже, не перехватывает никаких исключений, выдаваемых синхронным вызовом (this.Submit относится к синхронному методу).

public void Submit(FileInfo file, AnswerHandler callback)
{
    SubmitFileDelegate submitDelegate = new SubmitFileDelegate(this.Submit);
    submitDelegate.BeginInvoke(file, (IAsyncResult ar) =>
    {
        string result = submitDelegate.EndInvoke(ar);
        callback(result);
    }, null);
}

Есть ли способ перехватить исключение, созданное новым потоком, и отправить его исходному потоку? Кроме того, это «правильный» способ обработки асинхронных исключений? Я написал свой код, чтобы он мог быть назван так (при условии, что проблема исключений устранена):

try
{
    target.Submit(file, (response) =>
    {
        // do stuff
    });
}
catch (Exception ex)
{
    // catch stuff
}

но есть ли более правильный или элегантный способ сделать это?

Ответы [ 3 ]

9 голосов
/ 06 января 2012

Если вы ориентируетесь на .NET 4.0, вы можете использовать новую библиотеку параллельных задач и наблюдать свойство Exception объекта Task.

public Task Submit(FileInfo file)
{
    return Task.Factory.StartNew(() => DoSomething(file));
}

private void DoSomething(FileInfo file)
{
    throw new Exception();
}

Затем используйте его следующим образом:

Submit(myFileInfo).ContinueWith(task =>
{
    // Check task.Exception for any exceptions.

    // Do stuff with task.Result
});

где DoSomething - это метод, который вы хотите вызывать асинхронно, а делегат, который вы передаете ContinueWith, - ваш обратный вызов.

Дополнительную информацию об обработке исключений в TPL можно найтиздесь: http://msdn.microsoft.com/en-us/library/dd997415.aspx

8 голосов
/ 06 января 2012

Это не решение «наилучшей практики», но я думаю, что оно должно работать просто.

Вместо того, чтобы делегат был определен как

private delegate string SubmitFileDelegate(FileInfo file);

определить как

private delegate SubmitFileResult SubmitFileDelegate(FileInfo file);

и определите SubmitFileResult следующим образом:

public class SubmitFileResult
{
    public string Result;
    public Exception Exception;
}

Затем метод, который фактически выполняет отправку файла (не показан в вопросе), должен быть определен следующим образом:

private static SubmitFileResult Submit(FileInfo file)
{
    try
    {
        var submissionResult = ComplexSubmitFileMethod();

        return new SubmitFileResult { Result = submissionResult };
    }
    catch (Exception ex)
    {
        return new SubmitFileResult {Exception = ex, Result = "ERROR"};
    }
}

Таким образом, вы исследуете объект результата, посмотрите, не установлен ли он в поле «Результат» или «Исключение», и будете действовать соответственно.

3 голосов
/ 06 января 2012

Короче говоря, нет.

Когда вы вызываете submitDelegate.BeginInvoke, он порождает новый поток, возвращает и быстро выходит из блока try / catch (пока новый поток работает в фоновом режиме).

Вы можете, однако, перехватить все необработанные исключения, подобные этому:

AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(YourException);

Однако это отловит все в области приложения (не тольковаш асинхронный метод).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...