簡體   English   中英

異步等待和ContinueWith無法正常工作

[英]Async Await and ContinueWith not working as expected

我具有在.NET Standard 2.0上運行的以下代碼

public static Task<JobResult> TryRunAsync(this IJob job,
                                          CancellationToken cancellationToken = default(CancellationToken))
{
    return job.RunAsync(cancellationToken)
        .ContinueWith(t => {
             if (t.IsFaulted)
                 return JobResult.FromException(t.Exception.InnerException);
             if (t.IsCanceled)
                 return JobResult.Cancelled;

             return t.Result;
         });
}

而且我們注意到它沒有按預期運行。 我們認為當您等待對TryRun的調用時,它將始終調用可以處理異常/取消並返回作業結果的繼續。 我們希望減少創建的異步狀態機的數量。但是,事實並非如此。 這是一個較小的示例(創建一個新的.net core 2.0控制台應用程序並粘貼以下內容:

using System;
using System.Threading.Tasks;

namespace ConsoleApp4
{
    public class Program
    {
        public static async Task Main()
        {
            // works
            await DoStuff();
            Console.ReadKey();

            // blows up
            await TryRun();
            Console.ReadKey();
        }

        public static Task DoStuff()
        {
            return Method()
                .ContinueWith(t => Throws())
                .ContinueWith(t => {
                    if (t.IsFaulted)
                        Console.WriteLine("Faulted");
                    else if (t.IsCompletedSuccessfully)
                        Console.WriteLine("Success");
                });
        }

        public static Task Method()
        {
            Console.WriteLine("Method");
            return Task.CompletedTask;
        }

        public static Task TryRun()
        {
            return Throws()
                .ContinueWith(t => {
                    if (t.IsFaulted)
                        Console.WriteLine("Faulted");
                    else if (t.IsCompletedSuccessfully)
                        Console.WriteLine("Success");
                });
        }

        public static Task Throws()
        {
            Console.WriteLine("Throws");
            throw new ApplicationException("Grr");
        }
    }
}

您的<LangVersion>Latest</LangVersion>可能需要<LangVersion>Latest</LangVersion>

UPDATE

我們最終得到以下代碼:

public static Task<JobResult> TryRunAsync(this IJob job, 
                                           CancellationToken cancellationToken = default(CancellationToken))
{
    var tcs = new TaskCompletionSource<JobResult>(null);
    try {
        var task = job.RunAsync(cancellationToken);
        task.ContinueWith((task2, state2) => {
            var tcs2 = (TaskCompletionSource<object>)state2;
            if (task2.IsCanceled) {
                tcs2.SetResult(JobResult.Cancelled);
            } else if (task2.IsFaulted) {
                tcs2.SetResult(JobResult.FromException(task2.Exception));
            } else {
                tcs2.SetResult(JobResult.Success);
            }
        }, tcs, cancellationToken);
    } catch (Exception ex) {
        tcs.SetResult(JobResult.FromException(ex));
    }

    return tcs.Task;
}

方法throws實際上是在調用時引發異常,而不返回有故障的Task 您沒有要添加延續的Task 它甚至只是在到達ContinueWith調用之前就已經進入了調用堆棧。

實際上,這里沒有創建任何任務。 例如,如果執行了循環,則您將停留在相同的線程和相同的堆棧上。 通過在Throws方法中執行Task.FromException而不是throw,可以看到正確的行為。 同樣在核心2.1中,至少您可能會發現異步方法與延續版本一樣快甚至更快,並且分配的資源更少。 在嘗試優化狀態機之前,值得檢查您的跟蹤號。 同樣,如果您拋出異常,則狀態機絕對是您最不關心性能的問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM