163

VS2012 を取得し、ハンドルを取得しようとしていasyncます。

ブロッキング ソースから何らかの値を取得するメソッドがあるとします。メソッドの呼び出し元をブロックしたくありません。値が到着したときに呼び出されるコールバックを取得するメソッドを作成することもできますが、C# 5 を使用しているため、呼び出し元がコールバックを処理する必要がないようにメソッドを非同期にすることにしました。

// contrived example (edited in response to Servy's comment)
public static Task<string> PromptForStringAsync(string prompt)
{
    return Task.Factory.StartNew(() => {
        Console.Write(prompt);
        return Console.ReadLine();
    });
}

これを呼び出すメソッドの例を次に示します。非同期でない場合PromptForStringAsync、このメソッドはコールバック内にコールバックをネストする必要があります。async を使用すると、次のように非常に自然な方法でメソッドを記述できます。

public static async Task GetNameAsync()
{
    string firstname = await PromptForStringAsync("Enter your first name: ");
    Console.WriteLine("Welcome {0}.", firstname);

    string lastname = await PromptForStringAsync("Enter your last name: ");
    Console.WriteLine("Name saved as '{0} {1}'.", firstname, lastname);
}

ここまでは順調ですね。問題は、GetNameAsyncを呼び出すときです。

public static void DoStuff()
{
    GetNameAsync();
    MainWorkOfApplicationIDontWantBlocked();
}

要点GetNameAsyncは、非同期であるということです。できるだけ早く MainWorkOfApplicationIDontWantBlocked に戻り、GetNameAsync にバックグラウンドで実行させたいので、ブロックしたくありません。ただし、この方法で呼び出すと、次のGetNameAsync行でコンパイラの警告が表示されます。

Warning 1   Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

「呼び出しが完了する前に現在のメソッドの実行が続行される」ことを完全に認識しています。それが非同期コードのポイントですよね?

私は自分のコードを警告なしでコンパイルすることを好みますが、コードは意図した通りに動作しているため、ここで「修正」するものは何もありません。の戻り値を保存することで、警告を取り除くことができますGetNameAsync

public static void DoStuff()
{
    var result = GetNameAsync(); // supress warning
    MainWorkOfApplicationIDontWantBlocked();
}

しかし、今は余分なコードがあります。Visual Studio は、通常の "value never used" 警告を抑制するため、この不要なコードを書くことを余儀なくされたことを理解しているようです。

非同期ではないメソッドで GetNameAsync をラップすることで、警告を取り除くこともできます。

    public static Task GetNameWrapper()
    {
        return GetNameAsync();
    }

しかし、それはさらに余計なコードです。そのため、不要なコードを記述したり、不必要な警告を許容したりしなければなりません。

ここで間違っている async の使用について何かありますか?

4

10 に答える 10

117

結果が本当に必要ない場合は、GetNameAsyncの署名を return に変更するだけvoidです。

public static async void GetNameAsync()
{
    ...
}

関連する質問への回答を確認することを検討してください: void を返すことと Task を返すことの違いは何ですか?

アップデート

結果が必要な場合は、 を次のように変更しGetNameAsyncて返すことができTask<string>ます。

public static async Task<string> GetNameAsync()
{
    string firstname = await PromptForStringAsync("Enter your first name: ");
    string lastname = await PromptForStringAsync("Enter your last name: ");
    return firstname + lastname;
}

そして、次のように使用します。

public static void DoStuff()
{
    Task<string> task = GetNameAsync();

    // Set up a continuation BEFORE MainWorkOfApplicationIDontWantBlocked
    Task anotherTask = task.ContinueWith(r => {
            Console.WriteLine(r.Result);
        });

    MainWorkOfApplicationIDontWantBlocked();

    // OR wait for the result AFTER
    string result = task.Result;
}
于 2013-02-15T22:03:29.217 に答える
98

#pragmaこの議論にはかなり遅れていますが、プリプロセッサ ディレクティブを使用するオプションもあります。いくつかの条件で明示的に待機したくない非同期コードがあちこちにあり、残りの皆さんと同じように警告や未使用の変数が嫌いです。

#pragma warning disable 4014
SomeMethodAsync();
#pragma warning restore 4014

"4014"は、この MSDN ページからのものです: Compiler Warning (level 1) CS4014

https://stackoverflow.com/a/12145047/928483の @ryan-horath による警告/回答も参照してください。

待機していない非同期呼び出し中にスローされた例外は失われます。この警告を取り除くには、非同期呼び出しのタスクの戻り値を変数に割り当てる必要があります。これにより、スローされた例外に確実にアクセスでき、戻り値で示されます。

C# 7.0 の更新

C# 7.0 では、変数の破棄: Discards - C# Guideという新機能が追加されており、これもこの点で役立ちます。

_ = SomeMethodAsync();
于 2013-10-17T18:44:43.770 に答える
30

async void悪い!

  1. void を返すことと Task を返すことの違いは何ですか?
  2. https://jaylee.org/archive/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.html

私が提案するのはTask、匿名メソッドを介して明示的に実行することです...

例えば

public static void DoStuff()
{
    Task.Run(async () => GetNameAsync());
    MainWorkOfApplicationIDontWantBlocked();
}

または、ブロックしたい場合は、匿名メソッドで待機できます

public static void DoStuff()
{
    Task.Run(async () => await GetNameAsync());
    MainWorkOfApplicationThatWillBeBlocked();
}

ただし、GetNameAsyncメソッドがUIまたはUIにバインドされたものと対話する必要がある場合(WINRT/MVVM、私はあなたを見ています)、少しファンキーになります=)

このようにUIディスパッチャーへの参照を渡す必要があります...

Task.Run(async () => await GetNameAsync(CoreApplication.MainView.CoreWindow.Dispatcher));

そして、非同期メソッドで、UI または UI バインド要素とやり取りする必要があります。これは、ディスパッチャーと考えられています...

dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {  this.UserName = userName; });
于 2013-08-21T01:21:40.380 に答える
7

この警告に関する Microsoft の記事によると、返されたタスクを変数に割り当てるだけで解決できます。以下は、Microsoft の例で提供されているコードの翻訳です。

    // To suppress the warning without awaiting, you can assign the 
    // returned task to a variable. The assignment doesn't change how
    // the program runs. However, the recommended practice is always to
    // await a call to an async method.
    // Replace Call #1 with the following line.
    Task delayTask = CalledMethodAsync(delay);

これを行うと、ReSharper で「Local variable is never used」というメッセージが表示されることに注意してください。

于 2013-05-29T21:45:09.843 に答える
1

余分なコードを引き起こすのはあなたの単純化された例です。通常、プログラムのある時点でブロッキング ソースからフェッチされたデータを使用する必要があるため、データを取得できるように結果を返す必要があります。

プログラムの残りの部分から完全に分離された何かが実際に発生する場合、非同期は正しいアプローチではありません。そのタスクの新しいスレッドを開始するだけです。

于 2013-02-15T22:00:05.627 に答える