Самый простой способ сделать огонь и забыть метод в С#?

В 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)

Ответ 1

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

(пять лет спустя...)

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

как указано luisperezphd.

Ответ 2

Для С# 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());

Ответ 3

Чтобы добавить к ответьте, если это консольное приложение, просто введите 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();
    }
}

Ответ 4

Простым способом является создание и запуск потока без параметров lambda:

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

Используя этот метод над ThreadPool.QueueUserWorkItem, вы можете назвать свой новый поток, чтобы упростить его отладку. Кроме того, не забудьте использовать расширенную обработку ошибок в своей программе, потому что любые необработанные исключения за пределами отладчика внезапно сбой приложения:

введите описание изображения здесь

Ответ 5

Для .NET 4.5:

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

Ответ 6

Вызов beginInvoke и не catching EndInvoke не является хорошим подходом. Ответ прост: Причина, по которой вы должны вызвать EndInvoke, состоит в том, что результаты вызова (даже если нет возвращаемого значения) должны быть кэшированы .NET до тех пор, пока не будет вызван EndInvoke. Например, если вызываемый код генерирует исключение, исключение кэшируется в данных вызова. Пока вы не назовете EndInvoke, он останется в памяти. После вызова EndInvoke можно освободить память. В этом конкретном случае возможно, что память останется до тех пор, пока процесс не завершится, потому что данные поддерживаются внутренним кодом вызова. Я думаю, GC может в конечном итоге собрать его, но я не знаю, как GC узнает, что вы отказались от данных, а просто занимаете очень много времени, чтобы получить его. Я сомневаюсь, что это так. Следовательно, может произойти утечка памяти.

Подробнее можно найти на http://haacked.com/archive/2009/01/09/asynchronous-fire-and-forget-with-lambdas.aspx

Ответ 7

Рекомендуемый способ сделать это, когда вы используете 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.

Ссылка: Использование QueueBackgroundWorkItem для рассылки фоновых заданий из приложения ASP.NET в .NET 4.5.2

Ответ 8

Простейший подход .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 );  
}