168

私はWCFでそれらが[OperationContract(IsOneWay = true)]属性を持っているのを見ました。しかし、WCF は、ノンブロッキング関数を作成するためだけに、遅くて重いようです。理想的には static 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 でどのように機能するかについての適切な説明)

4

10 に答える 10

42

C# 4.0 以降については、Ade Miller によってここで最良の答えが与えられていることに私は感銘を受けました。

Task.Factory.StartNew(() => FireAway());

あるいは...

Task.Factory.StartNew(FireAway);

または...

new Task(FireAway).Start();

どこFireAwayですか

public static void FireAway()
{
    // Blah...
}

したがって、クラスとメソッド名の簡潔さのおかげで、これは、選択したものに応じて、スレッドプールバージョンよりも6〜19文字勝っています:)

ThreadPool.QueueUserWorkItem(o => FireAway());
于 2012-10-23T20:29:15.200 に答える
18

Will の answerに追加するには、これがコンソール アプリケーションの場合は、ワーカー スレッドが完了する前に終了するのを防ぐために、AutoResetEventと aを投げるだけです。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();
    }
}
于 2009-06-19T16:12:56.703 に答える
14

Asp.Net および .Net 4.5.2 を使用しているときにこれを行うには、.NET を使用することをお勧めします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();
});

または非同期を使用:

BackgroundTaskRunner.FireAndForgetTask(async () =>
{
    await FireAway();
});

これは、Azure Web サイトでうまく機能します。

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

于 2015-08-02T21:52:08.840 に答える
7

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

詳細については、http://haacked.com/archive/2009/01/09/asynchronous-fire-and-forget-with-lambdas.aspxを参照してください。

于 2010-01-17T07:17:03.847 に答える
5

最も単純な .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 );  
}
于 2009-12-09T02:51:50.290 に答える