インターフェイスがタスクを返さなければならない場合、操作なしの実装を行うための最良の方法は何ですか?


435

以下のコードでは、インターフェースのため、クラスLazyBarはそのメソッドからタスクを返す必要があります(引数のために変更することはできません)。もしLazyBarそれが迅速かつ同期的に実行するために起こるものでの実装は珍しいです-メソッドから無操作のタスクを返すための最良の方法は何ですか?

私はTask.Delay(0)以下に行きましたが、関数がたくさん呼び出された場合(引数のために、毎秒数百回と言う)、これがパフォーマンスの副作用を持っているかどうか知りたいです:

  • この構文糖は何か大きなものに巻き戻されますか?
  • アプリケーションのスレッドプールが詰まり始めますか?
  • コンパイラクリーバーはDelay(0)別の方法で処理するのに十分ですか?
  • return Task.Run(() => { });任意の異なること?

もっと良い方法はありますか?

using System.Threading.Tasks;

namespace MyAsyncTest
{
    internal interface IFooFace
    {
        Task WillBeLongRunningAsyncInTheMajorityOfImplementations();
    }

    /// <summary>
    /// An implementation, that unlike most cases, will not have a long-running
    /// operation in 'WillBeLongRunningAsyncInTheMajorityOfImplementations'
    /// </summary>
    internal class LazyBar : IFooFace
    {
        #region IFooFace Members

        public Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
        {
            // First, do something really quick
            var x = 1;

            // Can't return 'null' here! Does 'Task.Delay(0)' have any performance considerations?
            // Is it a real no-op, or if I call this a lot, will it adversely affect the
            // underlying thread-pool? Better way?
            return Task.Delay(0);

            // Any different?
            // return Task.Run(() => { });

            // If my task returned something, I would do:
            // return Task.FromResult<int>(12345);
        }

        #endregion
    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            Test();
        }

        private static async void Test()
        {
            IFooFace foo = FactoryCreate();
            await foo.WillBeLongRunningAsyncInTheMajorityOfImplementations();
            return;
        }

        private static IFooFace FactoryCreate()
        {
            return new LazyBar();
        }
    }
}


8
個人的に私は一緒に行きTask.FromResult<object>(null)ます。
CodesInChaos

回答:


624

Task.FromResult(0)or Task.FromResult<object>(null)を使用するTaskと、no-op式でを作成するよりもオーバーヘッドが少なくなります。Task結果が事前に決定されたを作成する場合、スケジューリングのオーバーヘッドは発生しません。


今日は、Task.CompletedTaskを使用してこれを実現することをお勧めします。


5
そして、あなたがgithub.com/StephenCleary/AsyncExを使用している場合、それらはTaskConstantsクラスを提供して、これらの完了したタスクをいくつかの他の非常に有用なタスク(0 int、true / false、Default <T>())と共に提供します
quentin-starin

5
return default(YourReturnType);
レジェンド

8
@Legendsそれは直接タスクを作成するためには機能しません
Reed Copsey

18
確かでTask.CompletedTaskはありませんが、トリックを行う可能性があります!(ただし.net 4.6が必要)
Peter

1
質問:Task.FromResult<TResult>を返すがどのようにして(Task<TResult>戻り値の型Task(paramsなし))を満たすのですか?
rory.ap 2018

187

の使用に関するReed Copseyの回答に追加するにTask.FromResultは、完了したタスクのすべてのインスタンスが同じであるため、すでに完了したタスクをキャッシュすると、パフォーマンスをさらに向上させることができます。

public static class TaskExtensions
{
    public static readonly Task CompletedTask = Task.FromResult(false);
}

ではTaskExtensions.CompletedTask、あなたは全体のアプリケーションドメイン全体で同じインスタンスを使用することができます。


.Net Framework(v4.6)最新バージョンはTask.CompletedTask静的プロパティでそれを追加します

Task completedTask = Task.CompletedTask;

返品するか、待つ必要がありますか?
Pixar

@Pixarどういう意味ですか?両方を実行できますが、それを待って同期的に続行されます。
i3arnon

申し訳ありませんが、私はコンテキストに言及する必要がありました:)今それを見ると、我々はpublic Task WillBeLongRunningAsyncInTheMajorityOfImplementations()同様に作ることができpublic async Task WillBeLongRunningAsyncInTheMajorityOfImplementations()ます。つまり、return CompletedTask;またはのいずれかawait CompletedTask;です。より好ましいものは何ですか(おそらくより効率的であるか、より一致していますか?)
ピクサー

3
@Pixarはっきりしない。「no-async」の方が効率的だと思いました。メソッドを非同期にすると、コンパイラーはメソッドをステートマシンに変換するように指示されます。また、呼び出すたびに新しいタスクが作成されます。すでに完了したタスクを返すと、より明確でパフォーマンスが向上します。
i3arnon

3
@Asadは割り当てを削減します(これによりGC時間も短縮されます)。新しいメモリを割り当て、完了したタスクが必要になるたびにタスクインスタンスを構築する代わりに、これを行うのは1回だけです。
i3arnon 2015

38

Task.Delay(0)受け入れられた回答は、完了したのキャッシュされたコピーであるため、良いアプローチでしたTask

4.6以降Task.CompletedTask、その目的がより明確になりましたがTask.Delay(0)、単一のキャッシュされたインスタンスを返すだけでなく、同じ単一のキャッシュされたインスタンスを返しますTask.CompletedTask

どちらもキャッシュされた性質は一定のままであることが保証されていますが、最適化として実装依存のみである実装依存最適化として(つまり、実装がまだ有効なものに変更された場合でも、それらは依然として正しく機能します)の使用Task.Delay(0)は受け入れられた答えよりも良い。


1
私はまだ4.5を使用していますが、調査を行ったところ、Task.Delay(0)が静的なCompletedTaskメンバーを返すための特別なケースであることを知って面白がっていました。それを自分の静的なCompletedTaskメンバーにキャッシュしました。:P
ダレンクラーク

2
理由はわかりませんがTask.CompletedTask、.NETのバージョンを4.6(プロファイル7)に設定してVS2017でテストしただけでも、PCLプロジェクトでは使用できません。
Felix

@Fay PCL APIサーフェスの一部であってはいけないと思いますが、現時点でPCLをサポートしているのは4.5もサポートしているので、自分で使用Task.CompletedTask => Task.Delay(0);する必要があるので、サポートしていません。私の頭の上から確かにわかりません。
Jon Hanna

17

最近これに遭遇し、メソッドが無効であることに関する警告/エラーを受け取り続けました。

私たちはコンパイラーを満足させるビジネスを行っており、これはそれを明らかにします:

    public async Task MyVoidAsyncMethod()
    {
        await Task.CompletedTask;
    }

これにより、ここまでのアドバイスの中で最も優れたものがまとめられます。メソッドで実際に何かを実行していない限り、returnステートメントは必要ありません。


17
それは完全に間違っています。メソッド定義に非同期が含まれているためコンパイラエラーが発生しているため、コンパイラは待機を予期しています。「正しい」使用法はpublic Task MyVoidAsyncMethog(){return Task.CompletedTask;}
Keith

3
これが最も
明確

3
キースのコメントのため。
noelicus

4
彼は完全に間違っているわけではなく、単にasyncキーワードを削除しただけです。私のアプローチはもっと慣用的です。彼はミニマルです。少し失礼でなければ。
Alexander Trauzzi 2017年

1
これは意味がありません。ここのキースに完全に同意します。実際にはすべての賛成票を取得するわけではありません。なぜ不要なコードを追加するのですか?public Task MyVoidAsyncMethod() {}上記の方法とまったく同じです。このように使用するユースケースがある場合は、追加のコードを追加してください。
Nick N.

12
return Task.CompletedTask; // this will make the compiler happy

9

指定したタイプを返す必要がある場合:

Task.FromResult<MyClass>(null);

3

Task completedTask = Task.CompletedTask;は.Net 4.6 のソリューションを好みますが、別のアプローチは、メソッドを非同期にマークしてvoidを返すことです。

    public async Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
    {
    }

警告(CS1998-待機式のない非同期関数)が表示されますが、このコンテキストでは無視しても安全です。


1
メソッドがvoidを返す場合、例外の問題が発生する可能性があります。
Adam Tuliper-MSFT 2018年

0

ジェネリックを使用している場合、すべての回答でコンパイルエラーが発生します。使用できますreturn default(T);。さらに説明するために、以下のサンプル。

public async Task<T> GetItemAsync<T>(string id)
            {
                try
                {
                    var response = await this._container.ReadItemAsync<T>(id, new PartitionKey(id));
                    return response.Resource;
                }
                catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
                {

                    return default(T);
                }

            }

なぜ反対票か。
Karthikeyan VK

問題は非同期メソッドに関するものではありませんでした:)
Frode Nilsen

0
return await Task.FromResult(new MyClass());

3
このコードは問題を解決する可能性がありますが、これが問題を解決する方法と理由の説明含めると、投稿の品質が向上し、おそらくより多くの投票が得られます。あなたが今尋ねている人だけでなく、あなたが将来の読者のための質問に答えていることを忘れないでください。回答を編集して説明を追加し、適用される制限と前提を示してください。
デビッドバック
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.