在C#中执行fire and forget方法的最简单方法是什么?

时间:2009-06-19 15:33:10

标签: c# .net nonblocking

我在WCF中看到他们有[OperationContract(IsOneWay = true)]属性。但是WCF似乎有点缓慢而且很重要,只是为了创建一个非阻塞功能。理想情况下会出现类似静态无阻塞MethodFoo(){}的内容,但我认为不存在。

在C#中创建非阻塞方法调用的最快方法是什么?

E.g。

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应用程序,你需要做一些事情来阻止并保持线程活着。否则,这可能会导致“忘记但永远不会实际执行”,在这种情况下,当然,根本不编写任何代码会更简单。 (A good description of how this works in ASP.NET

9 个答案:

答案 0 :(得分:240)

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

(五年后......)

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

正如luisperezphd所指出的那样。

答案 1 :(得分:34)

对于C#4.0及更新版本,让我觉得Ade Miller现在给出了最好的答案:Simplest way to do a fire and forget method in c# 4.0

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

甚至......

Task.Factory.StartNew(FireAway);
     

或者...

new Task(FireAway).Start();
     

FireAway

的位置
public static void FireAway()
{
    // Blah...
}
     

因此,凭借类和方法名称的简洁性,这就胜过了   线程池版本由6到19个字符组成,具体取决于   你选择的那个:)

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

答案 2 :(得分:17)

要添加到Will's answer,如果这是一个控制台应用程序,只需输入AutoResetEventWaitHandle以防止它在工作线程完成之前退出:

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

答案 3 :(得分:15)

对于.NET 4.5:

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

答案 4 :(得分:13)

一种简单的方法是使用无参数lambda创建和启动一个线程:

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

通过在ThreadPool.QueueUserWorkItem上使用此方法,您可以命名新线程以便于调试。另外,不要忘记在例程中使用大量的错误处理,因为调试器之外的任何未处理的异常都会突然导致应用程序崩溃:

enter image description here

答案 5 :(得分:10)

使用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网站上运行良好。

参考:Using QueueBackgroundWorkItem to Schedule Background Jobs from an ASP.NET Application in .NET 4.5.2

答案 6 :(得分:7)

调用beginInvoke而不是捕获EndInvoke不是一个好方法。答案很简单: 您应该调用EndInvoke的原因是,在调用EndInvoke之前,.NET必须缓存调用的结果(即使没有返回值)。例如,如果调用的代码抛出异常,则异常将缓存在调用数据中。在调用EndInvoke之前,它会保留在内存中。调用EndInvoke后,可以释放内存。对于这种特殊情况,内存可能会保留,直到进程关闭,因为数据是由调用代码在内部维护的。我想GC最终可能会收集它,但我不知道GC如何知道你已经放弃了数据而只是花了很长时间来检索它。我怀疑它确实如此。因此可能发生内存泄漏。

更多信息可以在http://haacked.com/archive/2009/01/09/asynchronous-fire-and-forget-with-lambdas.aspx

找到

答案 7 :(得分:2)

最简单的.NET 2.0及更高版本的方法是使用Asynchnonous编程模型(即委托上的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 );  
}

答案 8 :(得分:1)

近10年后:

Task.Run(FireAway);

我将在FireAway中添加异常处理和日志记录