我在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)
答案 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,如果这是一个控制台应用程序,只需输入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();
}
}
答案 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上使用此方法,您可以命名新线程以便于调试。另外,不要忘记在例程中使用大量的错误处理,因为调试器之外的任何未处理的异常都会突然导致应用程序崩溃:
答案 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中添加异常处理和日志记录