web-dev-qa-db-ja.com

await-asyncを使用するためのベストプラクティス、タスクをどこから開始しますか?

.NetWPFアプリケーションでawait/asyncメカニズムを使い始めました。

私のViewModelでは、サービスでasyncメソッドを呼び出しています。

私の質問は:

  1. このサービス内で直接、1つの大きなreturn await Task.Run(()=>{...});を作成します
  2. このサービスのすべてのサブメソッドも非同期であり、その中にTask.Runがありますか?

例:

1)

public class Service:IService{
    public async Task<SomeResult>(SomeParameter parameter){
        return await Task.Run(()=>{
            CopyStuff(parameter.A);
            UpgradeStuff(parameter.B);
            return ReloadStuff(parameter.C)
        });
    }

    private void CopyStuff(ParamA parameter){
        ...//Some long operation that will mainly wait on the disk

    }
    private void UpgradeStuff(ParamB parameter){
        ...//Some long operation that should not block the GUI thread
    }
    public SomeResult ReloadStuff(ParamC parameter){
        return ...;//Some long operation that relaunch some services and return their successs      
    }   
}

2)

public class Service:IService{
    public async Task<SomeResult>(SomeParameter parameter){
        await CopyStuff(parameter.A);
        await UpgradeStuff(parameter.B);
        return await ReloadStuff(parameter.C)       
    }

    private async Task CopyStuff(ParamA parameter){
        return await Task.Run(()=>{...});//Some long operation that will mainly wait on the disk
    }
    private async Task UpgradeStuff(ParamB parameter){
        return await Task.Run(()=>{...});//Some long operation that should not block the GUI thread
    }
    public async Task<SomeResult> ReloadStuff(ParamC parameter){
        return await Task.Run(()=>{return ...});//Some long operation that relaunch some services and return their successs 
    }   
}

私は両方のアプローチで利点を見ることができます:

  • 1)では、使用するタスクが少なくなります。これがおそらく最も効率的です(???)
  • 2)これは、async-awaitアプローチにより「準拠」しているように感じます。これにより、一部のメソッドの可視性を変更し、非同期のままにすることができます。これにより、必要に応じてメソッドを1日並行して実行できます。
15
J4N

どのオプションを選択しますか?

私はどちらのオプションも使用しません。どちらも誤解を招くAPIを作成します。サービスを使用するすべての人は、非同期メソッドを使用していると思いますが、真実は、偽の署名の背後にあるメソッドは実際にはまったく非同期ではないということです。
サービスは、メソッドの実行中にブロックされる別のThreadPoolスレッドに作業をプッシュするだけです。

この原則を使用するサーバー側ではそれほど悪くないように聞こえるクライアント側では、スケーラビリティが実際に損なわれる可能性があります。

スティーブンクリアリーによると:

メソッドの実装でTask.Runを使用しないでください。代わりに、Task.Runを使用してメソッドを呼び出します。

メソッドが本当に同期している場合は、サービスメソッドを偽の非同期署名でラップしないでください。重いメソッドの実行中にUIスレッドをブロックしたくない場合は、からサービスメソッドを呼び出すときにTask.Runを使用する必要があります。ビューモデル。

Stephen Clearyシリーズの Task.Runエチケットの記事 を彼のブログで読むことをお勧めします。

I/O操作に非同期メソッドを使用することを検討してください

さらに、サービスが実行する作業はCPUにバインドされた作業だけではないことがわかります。その場合、現在使用している同期で使用可能なものがある場合は、組み込みのI/O非同期APIメソッドの使用を検討する必要があります(たとえば、 非同期ファイルI/O )、この場合、メソッドは真の非同期メソッドになり、現在のような偽の非同期ラッパーではありません。

これを行うと、I/O非同期操作の実行中にUIスレッドがブロックされませんが、それでもCPUにバインドされた重い作業が含まれ、CPU中にUIをブロックしたくない場合バインドされた作業の実行では、ビューモデルからサービスメソッドを呼び出すときにTask.Runを引き続き使用できます(メソッドシグネチャはすでに非同期のものですが)。

上記の一連の記事で、同期メソッドと非同期メソッドの混合について詳しく説明します。

非同期APIメソッドでbultを使用するもう1つの大きな利点は、メソッドが本当に非同期である場合、非同期I/O操作の実行中などのThreadPoolスレッドのいずれかを ブロックしないでくださいThreadPoolスレッドは、他の作業を自由に行うことができます。
これは、サーバー側で非同期プログラミングを使用する場合に特に重要です(ただし、それだけではありません)。これにより、スケーラビリティが大幅に向上します。

非同期とMVVM

最後に、MVVMパターンに従っている場合 MSDN "async MVVM"の記事 は、使用できる優れた読み物です。

9
YuvShap