426

データをasync返さないメソッドがあります:

public async Task MyAsyncMethod()
{
    // do some stuff async, don't return any data
}

私はいくつかのデータを返す別のメソッドからこれを呼び出しています:

public string GetStringData()
{
    MyAsyncMethod(); // this generates a warning and swallows exceptions
    return "hello world";
}

MyAsyncMethod()待機せずに呼び出すと、Visual Studio で「この呼び出しは待機されていないため、呼び出しが完了する前に現在のメソッドが実行され続けます」という警告が表示されます。その警告のページには、次のように記載されています。

非同期呼び出しが完了するのを待ちたくなく、呼び出されたメソッドが例外を発生させないことが確実な場合にのみ、警告を抑制することを検討してください。

通話が完了するのを待ちたくないのは確かです。する必要もないし、する時間もない。ただし、呼び出しによって例外が発生する場合があります。

私はこの問題に何度か遭遇しましたが、共通の解決策が必要な共通の問題であると確信しています。

結果を待たずに非同期メソッドを安全に呼び出すにはどうすればよいですか?

アップデート:

結果を待つだけだと言っている人のために説明すると、これは Web サービス (ASP.NET Web API) で Web 要求に応答するコードです。UI コンテキストで待機すると UI スレッドが解放されますが、Web 要求呼び出しで待機すると、要求に応答する前にタスクが終了するまで待機するため、理由もなく応答時間が長くなります。

4

12 に答える 12

251

「非同期に」例外を取得したい場合は、次のようにします。

  MyAsyncMethod().
    ContinueWith(t => Console.WriteLine(t.Exception),
        TaskContinuationOptions.OnlyOnFaulted);

これにより、「メイン」スレッド以外のスレッドで例外を処理できます。 MyAsyncMethod()これは、を呼び出すスレッドからの呼び出しを「待つ」必要がないことを意味しますMyAsyncMethod。ただし、例外を使用して何かを実行することはできますが、例外が発生した場合に限ります。

アップデート:

技術的には、次のようにして同様のことができますawait

try
{
    await MyAsyncMethod().ConfigureAwait(false);
}
catch (Exception ex)
{
    Trace.WriteLine(ex);
}

try...これは、 / catch(またはusing)を具体的に使用する必要がある場合に役立ちますが、意味ContinueWithを知る必要があるため、もう少し明示的であることがわかりますConfigureAwait(false)

于 2013-03-20T12:59:55.417 に答える
87

最初にメソッドの作成を検討しGetStringData、それを から返されたタスクにする必要があります。asyncawaitMyAsyncMethod

MyAsyncMethod 例外を処理したり、いつ完了するかを知る必要がないと確信している場合は、次のようにすることができます。

public string GetStringData()
{
  var _ = MyAsyncMethod();
  return "hello world";
}

ところで、これは「一般的な問題」ではありません。一部のコードを実行したいが、それが完了するかどうかを気にせず、正常に完了するかどうかを気にしないということは非常にまれです

アップデート:

あなたは ASP.NET を使用していて、早期に復帰したいと考えているので、このテーマに関する私のブログ投稿が役立つかもしれません。ただし、ASP.NET はこのように設計されていないため、応答が返された後にコードが実行されるという保証はありません。ASP.NET は実行できるように最善を尽くしますが、保証はできません。

したがって、これは、イベントをログに放り込むような簡単なことの優れたソリューションです。これは、ビジネスに不可欠なあらゆる種類の操作に適したソリューションではありません。このような状況では、より複雑なアーキテクチャ採用し、操作を永続的に保存する方法 (Azure キュー、MSMQ など) と、それらを処理する別のバックグラウンド プロセス (Azure ワーカー ロール、Win32 サービスなど) を採用する必要があります。

于 2013-03-20T12:39:02.357 に答える
55

Peter Ritchieの答えは私が望んでいたものであり、ASP.NETの初期に戻ることに関するStephenClearyの記事は非常に役に立ちました。

ただし、より一般的な問題として(ASP.NETコンテキストに固有ではありません)、次のコンソールアプリケーションは、Peterの回答の使用法と動作を示しています。Task.ContinueWith(...)

static void Main(string[] args)
{
  try
  {
    // output "hello world" as method returns early
    Console.WriteLine(GetStringData());
  }
  catch
  {
    // Exception is NOT caught here
  }
  Console.ReadLine();
}

public static string GetStringData()
{
  MyAsyncMethod().ContinueWith(OnMyAsyncMethodFailed, TaskContinuationOptions.OnlyOnFaulted);
  return "hello world";
}

public static async Task MyAsyncMethod()
{
  await Task.Run(() => { throw new Exception("thrown on background thread"); });
}

public static void OnMyAsyncMethodFailed(Task task)
{
  Exception ex = task.Exception;
  // Deal with exceptions here however you want
}

GetStringData()待たずに早期に戻り、MyAsyncMethod()スローされた例外は/周辺ではなくMyAsyncMethod()で処理されOnMyAsyncMethodFailed(Task task)ますtrycatchGetStringData()

于 2013-03-20T15:49:46.090 に答える
25

私はこの解決策で終わります:

public async Task MyAsyncMethod()
{
    // do some stuff async, don't return any data
}

public string GetStringData()
{
    // Run async, no warning, exception are catched
    RunAsync(MyAsyncMethod()); 
    return "hello world";
}

private void RunAsync(Task task)
{
    task.ContinueWith(t =>
    {
        ILog log = ServiceLocator.Current.GetInstance<ILog>();
        log.Error("Unexpected Error", t.Exception);

    }, TaskContinuationOptions.OnlyOnFaulted);
}
于 2016-03-15T18:22:36.897 に答える
15

これはファイア アンド フォーゲットと呼ばれ、拡張機能があります。

タスクを消費し、何もしません。非同期メソッド内の非同期メソッドへのファイア アンド フォーゲット呼び出しに役立ちます。

nuget パッケージをインストールします。

使用する:

MyAsyncMethod().Forget();

編集:私が最近使用している別の方法があります:

_ = MyAsyncMethod();
于 2019-01-21T09:51:12.287 に答える
10

ベスト プラクティスではありません。これは避けてください。

ただし、「待機せずに C# で async メソッドを呼び出す」に対処するためだけに、async メソッドをTask.Run. このアプローチは、終了するまで待機しMyAsyncMethodます。

public string GetStringData()
{
    Task.Run(()=> MyAsyncMethod()).Result;
    return "hello world";
}

await非同期的にResultタスクのラップを解除しますが、単にResultを使用すると、タスクが完了するまでブロックされます。

ヘルパー クラスでラップする場合:

public static class AsyncHelper
{
    public static void Sync(Func<Task> func) => Task.Run(func).ConfigureAwait(false);

    public static T Sync<T>(Func<Task<T>> func) => Task.Run(func).Result;

}

のように呼び出します

public string GetStringData()
{
    AsyncHelper.Sync(() => MyAsyncMethod());
    return "hello world";
}
于 2019-11-01T05:18:14.250 に答える
8

ここでのパーティーに遅れましたが、他の回答で参照されていない素晴らしいライブラリを使用しています

https://github.com/brminnick/AsyncAwaitBestPractices

「Fire And Forget」が必要な場合は、タスクの拡張メソッドを呼び出します。

アクション onException を呼び出しに渡すと、両方の長所を確実に活用できます。例外を適切に処理する機能を維持しながら、実行を待機してユーザーを遅くする必要はありません。

あなたの例では、次のように使用します。

   public string GetStringData()
    {
        MyAsyncMethod().SafeFireAndForget(onException: (exception) =>
                    {
                      //DO STUFF WITH THE EXCEPTION                    
                    }); 
        return "hello world";
    }

また、すぐに使用できる ICommand を実装する awaitable AsyncCommands も提供します。これは、MVVM Xamarin ソリューションに最適です。

于 2019-12-01T08:24:00.183 に答える
1

疑問が生じると思いますが、なぜこれを行う必要があるのでしょうか。C# 5.0の理由はasync、結果を待つことができるようにするためです。このメソッドは実際には非同期ではありませんが、現在のスレッドにあまり干渉しないように一度に呼び出されるだけです。

おそらく、スレッドを開始して、それ自体で終了するようにしておく方がよい場合があります。

于 2013-03-20T12:02:29.673 に答える
1

メッセージ ループのあるテクノロジ (ASP がその 1 つかどうかは不明) では、タスクが終了するまでループをブロックしてメッセージを処理し、ContinueWith を使用してコードのブロックを解除できます。

public void WaitForTask(Task task)
{
    DispatcherFrame frame = new DispatcherFrame();
    task.ContinueWith(t => frame.Continue = false));
    Dispatcher.PushFrame(frame);
}

このアプローチは、ShowDialog をブロックし、UI の応答性を維持することに似ています。

于 2019-01-16T06:34:43.730 に答える