C#で火事を忘れてメソッドを忘れる最も簡単な方法は?
-
06-07-2019 - |
質問
WCFで、 [OperationContract(IsOneWay = true)]
属性があることを確認しました。しかし、WCFはノンブロッキング関数を作成するためだけに遅くて重いように見えます。 static void nonblocking MethodFoo(){}
のようなものが理想的ですが、それは存在しないと思います。
C#でノンブロッキングメソッド呼び出しを作成する最も簡単な方法は何ですか?
E.g。
class Foo
{
static void Main()
{
FireAway(); //No callback, just go away
Console.WriteLine("Happens immediately");
}
static void FireAway()
{
System.Threading.Thread.Sleep(5000);
Console.WriteLine("5 seconds later");
}
}
NB :これを読んでいる人は誰でも、メソッドを実際に終了させたいかどうかを考える必要があります。 (#2トップアンサーを参照)メソッドを終了する必要がある場合、ASP.NETアプリケーションなどの一部の場所では、スレッドをブロックして維持するために何かを行う必要があります。さもなければ、これは「火事を忘れるが、実際には実行しない」ことにつながる可能性があり、その場合、もちろん、コードをまったく記述しない方が簡単です。 ( ASP.NETでの動作の説明)
解決
ThreadPool.QueueUserWorkItem(o => FireAway());
(5年後...)
Task.Run(() => FireAway());
luisperezphd が指摘したとおり。
他のヒント
C#4.0以降では、ここでAde Millerが最良の回答を提供していることに気づきます: C#4.0で火災と忘却方法を行う最も簡単な方法
Task.Factory.StartNew(() => FireAway());
または偶数...
Task.Factory.StartNew(FireAway);
または...
new Task(FireAway).Start();
FireAway
の場所public static void FireAway() { // Blah... }
クラスとメソッド名の簡潔さにより、これは に応じて6〜19文字のスレッドプールバージョン 選択したもの:)
ThreadPool.QueueUserWorkItem(o => FireAway());
に追加するにはウィルの答え、これがコンソールアプリケーションの場合、ワーカースレッドが完了する前に終了しないように、 AutoResetEvent
と WaitHandle
をスローするだけです。
Using System;
Using System.Threading;
class Foo
{
static AutoResetEvent autoEvent = new AutoResetEvent(false);
static void Main()
{
ThreadPoolQueueUserWorkItem(new WaitCallback(FireAway), autoEvent);
autoEvent.WaitOne(); // Will wait for thread to complete
}
static void FireAway(object stateInfo)
{
System.Threading.Thread.Sleep(5000);
Console.WriteLine("5 seconds later");
((AutoResetEvent)stateInfo).Set();
}
}
.NET 4.5の場合:
Task.Run(() => FireAway());
簡単な方法は、パラメーターなしのラムダでスレッドを作成して開始することです:
(new Thread(() => {
FireAway();
MessageBox.Show("FireAway Finished!");
}) {
Name = "Long Running Work Thread (FireAway Call)",
Priority = ThreadPriority.BelowNormal
}).Start();
ThreadPool.QueueUserWorkItemでこのメソッドを使用することで、新しいスレッドに名前を付けてデバッグしやすくすることができます。また、デバッガーの外部で未処理の例外が発生するとアプリケーションが突然クラッシュするため、ルーチンで広範なエラー処理を使用することを忘れないでください。
Asp.Netおよび.Net 4.5.2を使用しているときにこれを行う推奨される方法は、 QueueBackgroundWorkItem
を使用することです。ヘルパークラスを次に示します。
public static class BackgroundTaskRunner
{
public static void FireAndForgetTask(Action action)
{
HostingEnvironment.QueueBackgroundWorkItem(cancellationToken => // .Net 4.5.2 required
{
try
{
action();
}
catch (Exception e)
{
// TODO: handle exception
}
});
}
/// <summary>
/// Using async
/// </summary>
public static void FireAndForgetTask(Func<Task> action)
{
HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken => // .Net 4.5.2 required
{
try
{
await action();
}
catch (Exception e)
{
// TODO: handle exception
}
});
}
}
使用例:
BackgroundTaskRunner.FireAndForgetTask(() =>
{
FireAway();
});
またはasyncを使用:
BackgroundTaskRunner.FireAndForgetTask(async () =>
{
await FireAway();
});
これは、Azure Webサイトでうまく機能します。
リファレンス:。NET 4.5.2のASP.NETアプリケーションからQueueBackgroundWorkItemを使用してバックグラウンドジョブをスケジュールする
beginInvokeを呼び出してEndInvokeをキャッチしないことは、適切なアプローチではありません。答えは簡単です: EndInvokeを呼び出す必要があるのは、EndInvokeが呼び出されるまで、呼び出しの結果(戻り値がなくても)を.NETでキャッシュする必要があるためです。たとえば、呼び出されたコードが例外をスローした場合、その例外は呼び出しデータにキャッシュされます。 EndInvokeを呼び出すまで、メモリに残ります。 EndInvokeを呼び出した後、メモリを解放できます。この特定のケースでは、データは呼び出しコードによって内部的に維持されるため、プロセスがシャットダウンするまでメモリが残る可能性があります。 GCは最終的にそれを収集する可能性があると思いますが、GCがデータを破棄したことと、取得に非常に長い時間を要したことをGCがどのように知るかはわかりません。疑わしい。したがって、メモリリークが発生する可能性があります。
詳細については、 httpをご覧ください。 ://haacked.com/archive/2009/01/09/asynchronous-fire-and-forget-with-lambdas.aspx
最も単純な.NET 2.0以降のアプローチは、非同期プログラミングモデル(つまり、デリゲートでのBeginInvoke)の使用です。
static void Main(string[] args)
{
new MethodInvoker(FireAway).BeginInvoke(null, null);
Console.WriteLine("Main: " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(5000);
}
private static void FireAway()
{
Thread.Sleep(2000);
Console.WriteLine("FireAway: " + Thread.CurrentThread.ManagedThreadId );
}
ほぼ10年後:
Task.Run(FireAway);
FireAway内に例外処理とロギングを追加します