Najprostszy sposób na zrobienie metody fire and forget w C#?

Widziałem w WCF, że mają atrybut [OperationContract(IsOneWay = true)]. Ale WCF wydaje się być powolne i ciężkie tylko po to, aby stworzyć funkcję nieblokującą. Idealnie byłoby coś w stylu static void nonblocking MethodFoo(){}, ale nie sądzę, że to istnieje.

Jaki jest najszybszy sposób, aby utworzyć non-blocking wywołanie metody w C#?

Np.

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 : każdy, kto to czyta, powinien zastanowić się, czy rzeczywiście chce zakończyć metodę. (Patrz # 2 top odpowiedź) jeśli metoda ma zakończyć, potem w niektórych miejscach, jak ASP.NET aplikacja, musisz zrobić coś, aby zablokować i utrzymać wątek przy życiu. W przeciwnym razie może to prowadzić do "fire-forget-but-never-actually-execute", w którym to przypadku, oczywiście, byłoby prościej napisać żaden kod w ogóle. (dobry opis jak to działa w ASP.NET )

Author: Wai Ha Lee, 2009-06-19

9 answers

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

(pięć lat później...)

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

Jak wskazał luisperezphd .

 215
Author: Will,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-10 13:45:19

Dla C# 4.0 i nowszych, uderza mnie, że najlepszą odpowiedź daje teraz Ade Miller: najprostszy sposób na zrobienie metody fire and forget w c# 4.0

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

Lub...

new Task(FireAway).Start();

Gdzie FireAway jest

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

Więc dzięki nazwie klasy i metody terseness to bije wersja threadpool o od sześciu do dziewiętnastu znaków w zależności od ten, który wybierzesz:)

ThreadPool.QueueUserWorkItem(o => FireAway());
 33
Author: Patrick Szalapski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:33

Aby dodać do odpowiedzi Willa , jeśli jest to aplikacja konsolowa, po prostu dodaj AutoResetEvent i WaitHandle, aby zapobiec jej zamknięciu przed zakończeniem wątku roboczego:

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();
    }
}
 15
Author: Kev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:29

Dla. NET 4.5:

Task.Run(() => FireAway());
 14
Author: David Murdoch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-23 07:34:29

Łatwym sposobem jest utworzenie i uruchomienie wątku z parametrem lambda:

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

Za pomocą tej metody przez ThreadPool.QueueUserWorkItem możesz nazwać swój nowy wątek, aby ułatwić debugowanie. Nie zapomnij również użyć rozbudowanej obsługi błędów w swojej rutynie, ponieważ wszelkie nieobsługiwane wyjątki poza debuggerem spowodują nagłą awarię aplikacji:

Tutaj wpisz opis obrazka

 12
Author: Robert Venables,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-10 13:27:17

Zalecany sposób robienia tego, gdy używasz Asp.Net i. Net 4.5.2 jest za pomocą QueueBackgroundWorkItem. Oto Klasa pomocnicza:

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
            }
        });
    }
}

Przykład użycia:

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

Lub używając async:

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

To działa świetnie na witrynach internetowych platformy Azure.

Reference: używanie QueueBackgroundWorkItem do planowania zadań w tle z ASP.NET aplikacja w. NET 4.5.2

 8
Author: Augusto Barreto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-04 04:58:24

Wywołanie beginInvoke i nie przechwycenie EndInvoke nie jest dobrym podejściem. Odpowiedź jest prosta: Powodem, dla którego powinieneś wywołać EndInvoke, jest to, że wyniki wywołania (nawet jeśli nie ma wartości zwracanej) muszą być buforowane przez.Net do czasu wywołania EndInvoke. Na przykład, jeśli wywołany kod wyrzuca wyjątek, to wyjątek jest buforowany w danych wywołania. Dopóki nie zadzwonisz EndInvoke, pozostanie w pamięci. Po wywołaniu EndInvoke pamięć może zostać zwolniona. W tym konkretnym przypadku możliwe, że pamięć pozostanie do momentu zamknięcia procesu, ponieważ dane są utrzymywane wewnętrznie przez kod wywołania. Myślę, że GC może w końcu go zebrać, ale nie wiem, skąd GC wiedziałby, że porzuciłeś dane vs. po prostu zajmuje naprawdę dużo czasu, aby je odzyskać. Wątpię. Stąd może wystąpić wyciek pamięci.

Więcej można znaleźć na http://haacked.com/archive/2009/01/09/asynchronous-fire-and-forget-with-lambdas.aspx

 7
Author: Manoj Aggarwal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-17 07:17:03

Najprostszym podejściem. NET 2.0 i późniejszym jest wykorzystanie asynchronicznego modelu programowania (tj. BeginInvoke on a delegate):

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 );  
}
 2
Author: Ash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-09 02:51:50

Prawie 10 lat później:

Task.Run(FireAway);

Dodałbym obsługę wyjątków i logowanie wewnątrz FireAway

 1
Author: Oscar Fraxedas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-16 19:13:52