web-dev-qa-db-ja.com

C#で火事を忘れてメソッドを忘れる最も簡単な方法は?

私はWCFで[OperationContract(IsOneWay = true)]属性を持っているのを見ました。しかし、WCFはノンブロッキング関数を作成するためだけに遅くて重いように見えます。理想的には、静的なvoid nonblocking MethodFoo(){}のようなものがありますが、それは存在しないと思います。

C#でノンブロッキングメソッド呼び出しを作成する最も簡単な方法は何ですか?

例えば。

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でこれがどのように機能するかの良い説明

132
MatthewMartin
ThreadPool.QueueUserWorkItem(o => FireAway());

(5年後...)

Task.Run(() => FireAway());

luisperezphd で指摘されているように。

250
Will

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());
37

Will's answer に追加するには、これがコンソールアプリケーションの場合は、AutoResetEventWaitHandleをスローして、ワーカースレッドが完了する前に終了しないようにします。

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();
    }
}
17
Kev

.NET 4.5の場合:

Task.Run(() => FireAway());
16
David Murdoch

簡単な方法は、パラメーターなしのラムダでスレッドを作成して開始することです。

(new Thread(() => { 
    FireAway(); 
    MessageBox.Show("FireAway Finished!"); 
}) { 
    Name = "Long Running Work Thread (FireAway Call)",
    Priority = ThreadPriority.BelowNormal 
}).Start();

ThreadPool.QueueUserWorkItemでこのメソッドを使用することにより、新しいスレッドに名前を付けてデバッグしやすくすることができます。また、デバッガーの外部で未処理の例外が発生するとアプリケーションが突然クラッシュするため、ルーチンで広範なエラー処理を使用することを忘れないでください。

enter image description here

14
Robert Venables

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サイトでうまく機能します。

参照: QueueBackgroundWorkItemを使用して、.NET 4.5.2のASP.NETアプリケーションからバックグラウンドジョブをスケジュールする

10
Augusto Barreto

BeginInvokeを呼び出してEndInvokeをキャッチしないのは、良いアプローチではありません。答えは簡単です:EndInvokeを呼び出す必要がある理由は、呼び出しの結果(戻り値がない場合でも)がEndInvokeが呼び出されるまで.NETによってキャッシュされる必要があるためです。たとえば、呼び出されたコードが例外をスローした場合、その例外は呼び出しデータにキャッシュされます。 EndInvokeを呼び出すまで、メモリに残ります。 EndInvokeを呼び出した後、メモリを解放できます。この特定のケースでは、データは呼び出しコードによって内部的に維持されるため、プロセスがシャットダウンするまでメモリが残る可能性があります。 GCは最終的にそれを収集するかもしれないと思いますが、データを取得するのに本当に長い時間をかけるのではなく、データを放棄したことをGCがどのように知るかわかりません。疑わしい。したがって、メモリリークが発生する可能性があります。

詳細は http://haacked.com/archive/2009/01/09/asynchronous-fire-and-forget-with-lambdas.aspx で見つけることができます。

7
Manoj Aggarwal

最も単純な.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 );  
}
3
Ash

ほぼ10年後:

Task.Run(FireAway);

FireAway内に例外処理とログを追加します

3
Oscar Fraxedas