Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/326.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 异步/等待方法和异常处理的最佳实践_C#_Wpf_Asynchronous_Exception Handling_Async Await - Fatal编程技术网

C# 异步/等待方法和异常处理的最佳实践

C# 异步/等待方法和异常处理的最佳实践,c#,wpf,asynchronous,exception-handling,async-await,C#,Wpf,Asynchronous,Exception Handling,Async Await,我必须处理一些使用异步函数不能正确运行的旧东西,由于我对这个概念的理解有限,我正在努力寻找处理以下问题的最佳方法 我有一个按钮,单击该按钮将执行一个长时间运行的任务(解压缩大型ZIP存档,需要几分钟)。其命令执行方法定义如下: private async void Import() { // some stuff tokenSource = new CancellationTokenSource(); IProgress<ProgressReport> pro

我必须处理一些使用异步函数不能正确运行的旧东西,由于我对这个概念的理解有限,我正在努力寻找处理以下问题的最佳方法

我有一个按钮,单击该按钮将执行一个长时间运行的任务(解压缩大型ZIP存档,需要几分钟)。其
命令
执行方法定义如下:

private async void Import()
{
    // some stuff
    tokenSource = new CancellationTokenSource();
    IProgress<ProgressReport> progress = new Progress<ProgressReport>(data => Report(data));

    try
    {
        await Task.Run(() =>
        {
            Backup(tokenSource.Token, progress);
            Unzip(tokenSource.Token, progress);
        });
    }
    catch(Exception)
    {
        // do some rollback operation
    }
private void Backup(CancellationToken token, IProgress<ProgressReport> progress)
{
token.ThrowIfCancellationRequested();

    var parent = Path.GetFullPath(Path.Combine(Paths.DataDirectory, ".."));
    if (!Directory.Exists(parent))
    {
        progress.Report(new ProgressReport(Level.Info, string.Format(
                "Root Directory ({0}) does not exist; Creating it.", parent)));
        Directory.CreateDirectory(parent);
        return;
    }

    if (!Directory.Exists(Paths.DataDirectory))
    {
        progress.Report(new ProgressReport(Level.Info, string.Format(
                "Data Directory ({0}) does not exist; nothing to backup.", Paths.DataDirectory)));
        return;
    }

    // Generate a name for the backup           
    try
    {
        progress.Report(new ProgressReport(Level.Info, string.Format(
                "Renaming source Data Directory ({0}) to a temporary name.", Paths.DataDirectory)));

        var temp = Path.Combine(parent, Guid.NewGuid().ToString("N"));
        Directory.Move(Paths.DataDirectory, temp);
        // Success, let's store the backupFolder in a class field
        backupFolder = temp;

        progress.Report(new ProgressReport(Level.Info, string.Format(
                "Source Data Directory ({0}) successfully renamed to {1}.", Paths.DataDirectory, backupFolder)));

        token.ThrowIfCancellationRequested();
    }
    catch (OperationCanceledException)
    {
        progress.Report(new ProgressReport(Level.Warn, "Cancelling Import Operation."));
        throw;
    }
    catch (Exception ex)
    {
        // some stuff then throw Exception to bubble-up
        throw;
    }
}
try
{
    await Backup(tokenSource.Token, progress);
    await Unzip(tokenSource.Token, progress);
}
private async Task Backup(CancellationToken token, IProgress<ProgressReport> progress)
{
    // Same logic
    Task.Delay(1000);
}

private async Task Unzip(CancellationToken token, IProgress<ProgressReport> progress)
{
    // Same logic
    Task.Delay(1000);
}
我的方法定义如下:

private async void Import()
{
    // some stuff
    tokenSource = new CancellationTokenSource();
    IProgress<ProgressReport> progress = new Progress<ProgressReport>(data => Report(data));

    try
    {
        await Task.Run(() =>
        {
            Backup(tokenSource.Token, progress);
            Unzip(tokenSource.Token, progress);
        });
    }
    catch(Exception)
    {
        // do some rollback operation
    }
private void Backup(CancellationToken token, IProgress<ProgressReport> progress)
{
token.ThrowIfCancellationRequested();

    var parent = Path.GetFullPath(Path.Combine(Paths.DataDirectory, ".."));
    if (!Directory.Exists(parent))
    {
        progress.Report(new ProgressReport(Level.Info, string.Format(
                "Root Directory ({0}) does not exist; Creating it.", parent)));
        Directory.CreateDirectory(parent);
        return;
    }

    if (!Directory.Exists(Paths.DataDirectory))
    {
        progress.Report(new ProgressReport(Level.Info, string.Format(
                "Data Directory ({0}) does not exist; nothing to backup.", Paths.DataDirectory)));
        return;
    }

    // Generate a name for the backup           
    try
    {
        progress.Report(new ProgressReport(Level.Info, string.Format(
                "Renaming source Data Directory ({0}) to a temporary name.", Paths.DataDirectory)));

        var temp = Path.Combine(parent, Guid.NewGuid().ToString("N"));
        Directory.Move(Paths.DataDirectory, temp);
        // Success, let's store the backupFolder in a class field
        backupFolder = temp;

        progress.Report(new ProgressReport(Level.Info, string.Format(
                "Source Data Directory ({0}) successfully renamed to {1}.", Paths.DataDirectory, backupFolder)));

        token.ThrowIfCancellationRequested();
    }
    catch (OperationCanceledException)
    {
        progress.Report(new ProgressReport(Level.Warn, "Cancelling Import Operation."));
        throw;
    }
    catch (Exception ex)
    {
        // some stuff then throw Exception to bubble-up
        throw;
    }
}
try
{
    await Backup(tokenSource.Token, progress);
    await Unzip(tokenSource.Token, progress);
}
private async Task Backup(CancellationToken token, IProgress<ProgressReport> progress)
{
    // Same logic
    Task.Delay(1000);
}

private async Task Unzip(CancellationToken token, IProgress<ProgressReport> progress)
{
    // Same logic
    Task.Delay(1000);
}
专用异步任务备份(CancellationToken令牌,IProgress进程)
{
//同样的逻辑
任务延迟(1000);
}
专用异步任务解压缩(CancellationToken令牌,IProgress进程)
{
//同样的逻辑
任务延迟(1000);
}

现在异常很好地出现在
Import
方法中,但是UI在整个作业完成时间内都是冻结的,就像作业是由UI线程处理的一样。有什么不对劲吗?

多亏了@i3arnon的评论,我终于实现了我想要的。 以下是我的电话是如何改变的:

await Task.Run(async () =>
{
    await Backup(tokenSource.Token, progress);
    await Unzip(tokenSource.Token, progress);
});
而且这两个函数仍然声明为
专用异步任务


这样,由于
任务,作业在后台运行。运行
,这样UI就不会冻结,异常就会正确出现。

您是否仍在使用
任务。运行
?看起来您将方法标记为
异步
,但它们是同步的,而不是异步的,但由于您没有显示它们的实现,具体说来,你做错了什么,无从得知。我用方法实现编辑了我的文章。不,我不再使用
任务。再运行
。@BastienM。如果希望这些方法在后台运行,则需要
Task.run
@BastienM。这是无关的。您可以使用
任务。运行
。在它内部调用一个异步方法并等待返回的任务。在这种情况下,异常将“冒泡”。