2

.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>csproj で必要になる場合があります。

アップデート

最終的に次のコードを使用しました。

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;
}
4

2 に答える 2

0

実際、ここでは作成されたタスクはありません。たとえば、ループを実行した場合、同じスレッドと同じスタックにとどまります。スローするのではなく、Throws メソッドで Task.FromException を実行すると、正しい動作を確認できます。また、少なくともコア 2.1 では、非同期メソッドが継続バージョンと同じかそれ以上に高速であり、割り当てが少ないことがわかる場合があります。ステート マシンを最適化する前に、トレース番号を確認する価値があります。また、例外をスローしている場合、ステート マシンは間違いなくパフォーマンスに関する懸念事項の中で最も少ないものです。

于 2017-12-18T17:37:15.080 に答える