私はこの質問が本当に好きです:
C#でfire and forgetメソッドを実行する最も簡単な方法は?
C#4.0にParallel拡張機能が追加されたので、Parallel linqでFire&Forgetを実行するためのよりクリーンな方法があることを知りたいだけです。
私はこの質問が本当に好きです:
C#でfire and forgetメソッドを実行する最も簡単な方法は?
C#4.0にParallel拡張機能が追加されたので、Parallel linqでFire&Forgetを実行するためのよりクリーンな方法があることを知りたいだけです。
回答:
4.0の答えではありませんが、.Net 4.5ではこれをさらに簡単にすることができます:
#pragma warning disable 4014
Task.Run(() =>
{
MyFireAndForgetMethod();
}).ConfigureAwait(false);
#pragma warning restore 4014
プラグマは、このタスクを実行して忘れることを通知する警告を無効にすることです。
中括弧内のメソッドがタスクを返す場合:
#pragma warning disable 4014
Task.Run(async () =>
{
await MyFireAndForgetMethod();
}).ConfigureAwait(false);
#pragma warning restore 4014
それを分解してみましょう:
Task.Runは、このコードがバックグラウンドで実行されることを通知するコンパイラ警告(CS4014を警告)を生成するTaskを返します-これはまさに望んだとおりなので、警告4014を無効にします。
デフォルトでは、タスクは「元のスレッドへのマーシャリング」を試みます。つまり、このタスクはバックグラウンドで実行され、それを開始したスレッドに戻ろうとします。多くの場合、元のスレッドが完了した後にタスクを実行して忘れることが終了します。これにより、ThreadAbortExceptionがスローされます。ほとんどの場合、これは無害です-それはあなたに言っているだけです、私は再参加しようとしました、私は失敗しましたが、とにかくあなたは気にしません。しかし、本番環境のログまたはローカル開発環境のデバッガーにThreadAbortExceptionsを含めることは、まだ少しうるさいです。.ConfigureAwait(false)
整頓されたままで、明示的に言う、これをバックグラウンドで実行する方法にすぎません。
これは冗長で、特に醜いプラグマなので、ライブラリメソッドを使用します。
public static class TaskHelper
{
/// <summary>
/// Runs a TPL Task fire-and-forget style, the right way - in the
/// background, separate from the current thread, with no risk
/// of it trying to rejoin the current thread.
/// </summary>
public static void RunBg(Func<Task> fn)
{
Task.Run(fn).ConfigureAwait(false);
}
/// <summary>
/// Runs a task fire-and-forget style and notifies the TPL that this
/// will not need a Thread to resume on for a long time, or that there
/// are multiple gaps in thread use that may be long.
/// Use for example when talking to a slow webservice.
/// </summary>
public static void RunBgLong(Func<Task> fn)
{
Task.Factory.StartNew(fn, TaskCreationOptions.LongRunning)
.ConfigureAwait(false);
}
}
使用法:
TaskHelper.RunBg(async () =>
{
await doSomethingAsync();
}
ConfigureAwait(false)
でTask.Run
、あなたがいない時にawait
タスクを。関数の目的はその名前にあります:「構成待ち」。await
タスクを行わない場合、継続は登録されず、タスクが「元のスレッドにマーシャリングして戻す」ためのコードはありません。より大きなリスクは、ファイナライザスレッドで監視されない例外が再スローされることです。この回答では対処していません。
#Disable Warning BC42358
ではTask
クラスはい、しかしPLINQは、コレクション上で照会のために実際にあります。
次のようなものはタスクでそれを行います。
Task.Factory.StartNew(() => FireAway());
あるいは...
Task.Factory.StartNew(FireAway);
または...
new Task(FireAway).Start();
どこFireAway
に
public static void FireAway()
{
// Blah...
}
したがって、クラスとメソッド名の簡潔さのおかげで、これはスレッドプールのバージョンよりも、選択した文字に応じて6〜19文字ほど優れています。
ThreadPool.QueueUserWorkItem(o => FireAway());
fire and forget in ASP.NET WebForms and windows.close()
?
この質問の主な答えにはいくつか問題があります。
1つ目は、本当の火と忘却の状況では、おそらくawait
タスクを実行しないため、を追加しても意味がありませんConfigureAwait(false)
。そうしない場合await
で返された値をConfigureAwait
、それはおそらく何の効果を持つことができません。
次に、タスクが例外で完了したときに何が起こるかを認識する必要があります。@ ade-millerが提案した簡単な解決策を考えてみましょう:
Task.Factory.StartNew(SomeMethod); // .NET 4.0
Task.Run(SomeMethod); // .NET 4.5
この発表の危険は:から未処理の例外が逃げる場合SomeMethod()
、その例外は認められことはありません、とも1は、アプリケーションをクラッシュ、ファイナライザスレッドに再スローします。したがって、ヘルパーメソッドを使用して、結果として生じる例外が確実に監視されるようにすることをお勧めします。
あなたはこのようなものを書くことができます:
public static class Blindly
{
private static readonly Action<Task> DefaultErrorContinuation =
t =>
{
try { t.Wait(); }
catch {}
};
public static void Run(Action action, Action<Exception> handler = null)
{
if (action == null)
throw new ArgumentNullException(nameof(action));
var task = Task.Run(action); // Adapt as necessary for .NET 4.0.
if (handler == null)
{
task.ContinueWith(
DefaultErrorContinuation,
TaskContinuationOptions.ExecuteSynchronously |
TaskContinuationOptions.OnlyOnFaulted);
}
else
{
task.ContinueWith(
t => handler(t.Exception.GetBaseException()),
TaskContinuationOptions.ExecuteSynchronously |
TaskContinuationOptions.OnlyOnFaulted);
}
}
}
この実装のオーバーヘッドは最小限である必要があります。継続は、タスクが正常に完了しない場合にのみ呼び出され、同期で呼び出される必要があります(元のタスクとは別にスケジュールされるのではありません)。「レイジー」の場合、継続デリゲートの割り当ても発生しません。
非同期操作のキックオフは簡単になります。
Blindly.Run(SomeMethod); // Ignore error
Blindly.Run(SomeMethod, e => Log.Warn("Whoops", e)); // Log error
1.これは.NET 4.0のデフォルトの動作でした。.NET 4.5では、デフォルトの動作が変更され、監視されていない例外がファイナライザスレッドで再スローされないようになりました(ただし、TaskSchedulerのUnobservedTaskExceptionイベントを介してそれらを引き続き監視できます)。ただし、デフォルトの構成は上書き可能であり、アプリケーションに.NET 4.5が必要な場合でも、監視されていないタスクの例外が無害であると想定しないでください。
ContinueWith
キャンセルによりが呼び出された場合、前件の例外プロパティはNullになり、null参照例外がスローされます。タスク継続オプションをに設定OnlyOnFaulted
すると、nullチェック、または例外を使用する前に例外がnullかどうかをチェックする必要がなくなります。
Task
は実装の詳細になります。
Task
」は、「バックグラウンド操作を開始する簡単な方法が欲しい、その結果を決して観察したくない、どのメカニズムが使用されているかを気にしていない」と同じではありません。それをするために。" その上で、私は尋ねられた質問に対する私の回答を後回しにしています。
fire and forget
にはASP.NET Webフォームとwindows.close()
?
Mike Strobelの回答で発生する問題を修正するだけです。
を使用しvar task = Task.Run(action)
、そのタスクに継続を割り当てた後Task
、例外ハンドラの継続をに割り当てる前に、いくつかの例外をスローするリスクに遭遇しますTask
。したがって、以下のクラスにはこのリスクがないはずです。
using System;
using System.Threading.Tasks;
namespace MyNameSpace
{
public sealed class AsyncManager : IAsyncManager
{
private Action<Task> DefaultExeptionHandler = t =>
{
try { t.Wait(); }
catch { /* Swallow the exception */ }
};
public Task Run(Action action, Action<Exception> exceptionHandler = null)
{
if (action == null) { throw new ArgumentNullException(nameof(action)); }
var task = new Task(action);
Action<Task> handler = exceptionHandler != null ?
new Action<Task>(t => exceptionHandler(t.Exception.GetBaseException())) :
DefaultExeptionHandler;
var continuation = task.ContinueWith(handler,
TaskContinuationOptions.ExecuteSynchronously
| TaskContinuationOptions.OnlyOnFaulted);
task.Start();
return continuation;
}
}
}
ここでは、task
は直接実行されず、作成され、継続が割り当てられます。継続が割り当てられる前に、タスクが実行される(または例外がスローされる)リスクを排除するためにタスクが実行されます。
Run
ここのメソッドは継続を返すTask
ので、実行が完了したことを確認する単体テストを作成できます。ただし、使用方法では無視してかまいません。