Ответ 1
ThreadPool.QueueUserWorkItem(o => FireAway());
(пять лет спустя...)
Task.Run(() => FireAway());
как указано luisperezphd.
В WCF я видел атрибут [OperationContract(IsOneWay = true)]
. Но WCF кажется медленным и тяжелым, чтобы создать неблокирующую функцию. В идеале было бы что-то вроде статического void nonblocking MethodFoo(){}
, но я не думаю, что это существует.
Каков самый быстрый способ создания вызова неблокирующего метода в С#?
например.
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());
(пять лет спустя...)
Task.Run(() => FireAway());
как указано luisperezphd.
Для С# 4.0 и новее мне кажется, что лучший ответ теперь дается здесь Аде Миллер: Простейший способ сделать огонь и забыть метод в С# 4.0
Task.Factory.StartNew(() => FireAway());
Или даже...
Task.Factory.StartNew(FireAway);
Или...
new Task(FireAway).Start();
Где
FireAway
естьpublic static void FireAway() { // Blah... }
Таким образом, в силу краткости имени класса и метода это превосходит версия threadpool между шестью и девятнадцатью символами в зависимости от тот, который вы выберете:)
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();
}
}
Простым способом является создание и запуск потока без параметров lambda:
(new Thread(() => {
FireAway();
MessageBox.Show("FireAway Finished!");
}) {
Name = "Long Running Work Thread (FireAway Call)",
Priority = ThreadPriority.BelowNormal
}).Start();
Используя этот метод над ThreadPool.QueueUserWorkItem, вы можете назвать свой новый поток, чтобы упростить его отладку. Кроме того, не забудьте использовать расширенную обработку ошибок в своей программе, потому что любые необработанные исключения за пределами отладчика внезапно сбой приложения:
Для .NET 4.5:
Task.Run(() => FireAway());
Вызов beginInvoke и не catching EndInvoke не является хорошим подходом. Ответ прост: Причина, по которой вы должны вызвать EndInvoke, состоит в том, что результаты вызова (даже если нет возвращаемого значения) должны быть кэшированы .NET до тех пор, пока не будет вызван EndInvoke. Например, если вызываемый код генерирует исключение, исключение кэшируется в данных вызова. Пока вы не назовете EndInvoke, он останется в памяти. После вызова EndInvoke можно освободить память. В этом конкретном случае возможно, что память останется до тех пор, пока процесс не завершится, потому что данные поддерживаются внутренним кодом вызова. Я думаю, GC может в конечном итоге собрать его, но я не знаю, как GC узнает, что вы отказались от данных, а просто занимаете очень много времени, чтобы получить его. Я сомневаюсь, что это так. Следовательно, может произойти утечка памяти.
Подробнее можно найти на http://haacked.com/archive/2009/01/09/asynchronous-fire-and-forget-with-lambdas.aspx
Рекомендуемый способ сделать это, когда вы используете 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.
Простейший подход .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 );
}