最简单的方法来做一个火和忘记方法在 C # ?

我看到在周转基金,他们有 [OperationContract(IsOneWay = true)]属性。但是 WCF 看起来有点缓慢和沉重,仅仅是为了创建一个非阻塞函数。理想情况下,会有类似静态 void 非阻塞 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");
}
}

NB : 每个读这篇文章的人都应该考虑一下他们是否真的希望这个方法能够完成。如果方法必须完成,那么在某些地方,比如 ASP.NET 应用程序,您需要做一些事情来阻塞线程并保持线程活动。否则,这可能导致“发射-忘记-但从来没有实际执行”,在这种情况下,当然,更简单的做法是根本不编写代码。(很好地描述了它在 ASP.NET 中是如何工作的)

112854 次浏览
ThreadPool.QueueUserWorkItem(o => FireAway());

(五年后...)

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

正如 Luisperezphd指出的。

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

如果这是一个控制台应用,只需要在工作线程完成之前添加一个 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();
}
}

最简单的.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 );
}

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

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

对于 C # 4.0以及更新的版本,Ade Miller 给出了最好的答案: C # 4.0中最简单的生火和遗忘方法

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

甚至..。

Task.Factory.StartNew(FireAway);

或者..。

new Task(FireAway).Start();

FireAway在哪里

public static void FireAway()
{
// Blah...
}

因此,凭借类和方法名的简洁性,这打败了 线程池版本由六至十九个字符之间取决于 你选择的那个:)

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

对于.NET 4.5:

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

在使用 Asp 时推荐的方法。网和。净值4.5.2是使用 QueueBackgroundWorkItem。下面是一个 helper 类:

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 网站上非常有用。

参考资料: 在.NET 4.5.2中使用 QueueBackoundWorkItem 从 ASP.NET 应用程序调度后台作业

近10年后:

Task.Run(FireAway);

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

如果您想在控制台中进行测试,请记住在控制台被 Press any key to continue ...丢失线程之前需要 Console.ReadKey()或类似的东西

public static void Main()
{
Task.Factory.StartNew(async () =>
{
await LongTaskAsync();
}, TaskCreationOptions.LongRunning).ConfigureAwait(false);
    

Console.WriteLine("Starts immediately");
Console.ReadKey();
}


static async Task LongTaskAsync()
{
await Task.Delay(5000);
Console.WriteLine("After 5 seconds delay");
}

最简单的方法就是使用抛弃模式:

_ = MyFireAndForgetTask(myParameters);

这将通知您的方法不需要 Task 的结果,并且线程的执行不会停止。

请注意,Task 必须调用 Task。使用此模式在其中运行以实现异步。以我们之前的方法为例:

Task MyFireAndForgetTask(myParameters)
{
return Task.Run(/* put Task, Func<T>, or Action here*/);
}

如果忽略此步骤,则任务将同步运行,并且不会按预期的方式运行。

此外,还可以使用赋值模式。这对于方法运行到最后一行但挂起到 Task 完成的情况非常有用。我们会利用任务。等一下。以我们之前的方法为例:

void MyCustomEventHandler(object sender, EventArgs args)
{
/* perform some code here */
var myTask = MyFireAndForgetTask(myParameters);
/* perform some more code here; thread is not blocked */


/// Hang the method until the Task is completed.
/// using "await myTask;" is equivalent.
myTask.Wait();
}

这将执行一个“发射并且忘记直到完成”,这在某些平台(即 ASP.NET)上是强制性的。