代码之家  ›  专栏  ›  技术社区  ›  MatthewMartin muthu

在C中使用最简单的方法进行“火与忘”方法。

  •  123
  • MatthewMartin muthu  · 技术社区  · 15 年前

    我在WCF看到他们有 [OperationContract(IsOneWay = true)] 属性。但是,仅仅为了创建一个非阻塞函数,wcf看起来有点慢而且重。理想情况下会有类似静态空穴非阻塞的东西 MethodFoo(){} 但我不认为这存在。

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

    例如。

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

    :阅读本文的每个人都应该考虑是否真的希望该方法完成。(请参见2答案)如果该方法必须完成,那么在某些地方(如ASP.NET应用程序),您将需要执行一些操作来阻止线程并使其保持活动状态。否则,这可能会导致“fire-forget但永远不会实际执行”,在这种情况下,当然,不编写任何代码会更简单。( A good description of how this works in ASP.NET )

    9 回复  |  直到 6 年前
        1
  •  231
  •   Will    6 年前
    ThreadPool.QueueUserWorkItem(o => FireAway());
    

    (五年后……)

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

    正如所指出的 luisperezphd .

        2
  •  34
  •   Community CDub    7 年前

    对于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());
    
        3
  •  16
  •   Community CDub    7 年前

    添加到 Will's answer ,如果这是一个控制台应用程序,只需将 AutoResetEvent 和A 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
  •  15
  •   nawfal Donny V.    9 年前

    .NET 4.5:

    Task.Run(() => FireAway());
    
        5
  •  13
  •   josliber Martin Ballet    9 年前

    一种简单的方法是使用无参数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

        6
  •  10
  •   Augusto Barreto    9 年前

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

    或使用异步:

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

    这在Azure网站上很有用。

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

        7
  •  7
  •   Manoj Aggarwal    15 年前

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

    有关更多信息,请访问 http://haacked.com/archive/2009/01/09/asynchronous-fire-and-forget-with-lambdas.aspx

        8
  •  2
  •   Ash    15 年前

    最简单的.NET 2.0和更高版本的方法是使用Asynchnous编程模型(即委托上的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 );  
    }
    
        9
  •  1
  •   Oscar Fraxedas    6 年前

    近10年后:

    Task.Run(FireAway);
    

    我会在Fireaway中添加异常处理和日志记录