任务发生异常时,根据用户输入多次重试任务

时间:2023-02-03
本文介绍了任务发生异常时,根据用户输入多次重试任务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

问题描述

我的应用程序中的所有服务调用都是作为任务实现的.当某个任务出现故障时,我需要向用户显示一个对话框来重试上次失败的操作.如果用户选择重试,程序应该重试该任务,否则程序的执行应该在记录异常后继续.任何人对如何实现这个功能有一个高层次的想法?

All the service calls in my application are implemented as tasks.When ever a task is faulted ,I need to present the user with a dialog box to retry the last operation failed.If the user chooses retry the program should retry the task ,else the execution of the program should continue after logging the exception.Any one has got a high level idea on how to implement this functionality ?

推荐答案

UPDATE 5/2017

C# 6 异常过滤器使 catch 子句更加简单:

C# 6 exception filters make the catch clause a lot simpler :

    private static async Task<T> Retry<T>(Func<T> func, int retryCount)
    {
        while (true)
        {
            try
            {
                var result = await Task.Run(func);
                return result;
            }
            catch when (retryCount-- > 0){}
        }
    }

还有一个递归版本:

    private static async Task<T> Retry<T>(Func<T> func, int retryCount)
    {
        try
        {
            var result = await Task.Run(func);
            return result;
        }
        catch when (retryCount-- > 0){}
        return await Retry(func, retryCount);
    }

原创

编写重试函数的方法有很多种:您可以使用递归或任务迭代.不久前,希腊 .NET 用户组中有一个 讨论做到这一点的不同方法.
如果您使用 F#,您还可以使用 Async 构造.不幸的是,您至少不能在 Async CTP 中使用 async/await 构造,因为编译器生成的代码不喜欢多个等待或可能在 catch 块中重新抛出.

There are many ways to code a Retry function: you can use recursion or task iteration. There was a discussion in the Greek .NET User group a while back on the different ways to do exactly this.
If you are using F# you can also use Async constructs. Unfortunately, you can't use the async/await constructs at least in the Async CTP, because the code generated by the compiler doesn't like multiple awaits or possible rethrows in catch blocks.

递归版本可能是在 C# 中构建重试的最简单方法.以下版本不使用 Unwrap 并在重试之前添加可选延迟:

The recursive version is perhaps the simplest way to build a Retry in C#. The following version doesn't use Unwrap and adds an optional delay before retries :

private static Task<T> Retry<T>(Func<T> func, int retryCount, int delay, TaskCompletionSource<T> tcs = null)
    {
        if (tcs == null)
            tcs = new TaskCompletionSource<T>();
        Task.Factory.StartNew(func).ContinueWith(_original =>
        {
            if (_original.IsFaulted)
            {
                if (retryCount == 0)
                    tcs.SetException(_original.Exception.InnerExceptions);
                else
                    Task.Factory.StartNewDelayed(delay).ContinueWith(t =>
                    {
                        Retry(func, retryCount - 1, delay,tcs);
                    });
            }
            else
                tcs.SetResult(_original.Result);
        });
        return tcs.Task;
    } 

StartNewDelayed 函数来自ParallelExtensionsExtras 采样并使用计时器触发超时发生时的TaskCompletionSource.

The StartNewDelayed function comes from the ParallelExtensionsExtras samples and uses a timer to trigger a TaskCompletionSource when the timeout occurs.

F# 版本要简单得多:

The F# version is a lot simpler:

let retry (asyncComputation : Async<'T>) (retryCount : int) : Async<'T> = 
let rec retry' retryCount = 
    async {
        try
            let! result = asyncComputation  
            return result
        with exn ->
            if retryCount = 0 then
                return raise exn
            else
                return! retry' (retryCount - 1)
    }
retry' retryCount

不幸的是,在 C# 中使用 Async CTP 中的 async/await 编写类似的东西是不可能的,因为编译器不喜欢 catch 块中的 await 语句.以下尝试也静默失败,因为运行时不喜欢在异常后遇到等待:

Unfortunatley, it isn't possible to write something similar in C# using async/await from the Async CTP because the compiler doesn't like await statements inside a catch block. The following attempt also fails silenty, because the runtime doesn't like encountering an await after an exception:

private static async Task<T> Retry<T>(Func<T> func, int retryCount)
    {
        while (true)
        {
            try
            {
                var result = await TaskEx.Run(func);
                return result;
            }
            catch 
            {
                if (retryCount == 0)
                    throw;
                retryCount--;
            }
        }
    }

关于询问用户,可以修改Retry,调用一个询问用户并通过TaskCompletionSource返回任务的函数,在用户回答时触发下一步,例如:

As for asking the user, you can modify Retry to call a function that asks the user and returns a task through a TaskCompletionSource to trigger the next step when the user answers, eg:

 private static Task<bool> AskUser()
    {
        var tcs = new TaskCompletionSource<bool>();
        Task.Factory.StartNew(() =>
        {
            Console.WriteLine(@"Error Occured, continue? YN");
            var response = Console.ReadKey();
            tcs.SetResult(response.KeyChar=='y');

        });
        return tcs.Task;
    }

    private static Task<T> RetryAsk<T>(Func<T> func, int retryCount,  TaskCompletionSource<T> tcs = null)
    {
        if (tcs == null)
            tcs = new TaskCompletionSource<T>();
        Task.Factory.StartNew(func).ContinueWith(_original =>
        {
            if (_original.IsFaulted)
            {
                if (retryCount == 0)
                    tcs.SetException(_original.Exception.InnerExceptions);
                else
                    AskUser().ContinueWith(t =>
                    {
                        if (t.Result)
                            RetryAsk(func, retryCount - 1, tcs);
                    });
            }
            else
                tcs.SetResult(_original.Result);
        });
        return tcs.Task;
    } 

通过所有的延续,您可以看到为什么异步版本的 Retry 如此受欢迎.

With all the continuations, you can see why an async version of Retry is so desirable.

更新:

在 Visual Studio 2012 Beta 中,以下两个版本有效:

In Visual Studio 2012 Beta the following two versions work:

带有while循环的版本:

A version with a while loop:

    private static async Task<T> Retry<T>(Func<T> func, int retryCount)
    {
        while (true)
        {
            try
            {
                var result = await Task.Run(func);
                return result;
            }
            catch
            {
                if (retryCount == 0)
                    throw;
                retryCount--;
            }
        }
    }

还有一个递归版本:

    private static async Task<T> Retry<T>(Func<T> func, int retryCount)
    {
        try
        {
            var result = await Task.Run(func);
            return result;
        }
        catch
        {
            if (retryCount == 0)
                throw;
        }
        return await Retry(func, --retryCount);
    }

这篇关于任务发生异常时,根据用户输入多次重试任务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

上一篇:我应该从 .NET 中的 Exception 或 ApplicationException 派生自定义异常吗? 下一篇:我怎样才能捕捉到 404?

相关文章

最新文章