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 )
9 answers
ThreadPool.QueueUserWorkItem(o => FireAway());
(pięć lat później...)
Task.Run(() => FireAway());
Jak wskazał luisperezphd .
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
Albo nawet...Task.Factory.StartNew(() => FireAway());
Task.Factory.StartNew(FireAway);
Lub...
new Task(FireAway).Start();
Gdzie
FireAway
jestpublic 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());
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();
}
}
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());
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:
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
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
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 );
}
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
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