在ASP.NET中正确实现后台进程线程

19
我需要执行一个无限的 while 循环,并希望在 global.asax 中启动执行。 我的问题是应该如何做?应该启动一个新线程还是使用 Async 和 Task 或其他任何东西?在 while 循环内部,我需要执行 await TaskEx.Delay(5000);。 如何实现这一点,以便不会阻塞任何其他进程并且不会创建内存泄漏? 我使用 VS10、AsyncCTP3、MVC4
 public void SignalRConnectionRecovery()
        {
            while (true)
            {
                Clients.SetConnectionTimeStamp(DateTime.UtcNow.ToString());
                await TaskEx.Delay(5000);
            }
        }

我只需要将它作为全局单例实例在应用程序可用的情况下运行即可。
编辑:已解决。
这是Global.asax中的最终解决方案。
protected void Application_Start()
{
    Thread signalRConnectionRecovery = new Thread(SignalRConnectionRecovery);
    signalRConnectionRecovery.IsBackground = true;
    signalRConnectionRecovery.Start();

    Application["SignalRConnectionRecovery"] = signalRConnectionRecovery;
}


protected void Application_End()
{
    try
    {
        Thread signalRConnectionRecovery = (Thread)Application["SignalRConnectionRecovery"];
        if (signalRConnectionRecovery != null && signalRConnectionRecovery.IsAlive)
        {
            signalRConnectionRecovery.Abort();
        }
    }
    catch
    {
            ///
    }
}

我找到了一篇关于如何使用异步工作器的好文章: http://www.dotnetfunda.com/articles/article613-background-processes-in-asp-net-web-applications.aspx 还有这个链接: http://code.msdn.microsoft.com/CSASPNETBackgroundWorker-dda8d7b6 但是我认为对于我的需求,这个更完美: http://forums.asp.net/t/1433665.aspx/1

1
Clients.SetConnectionTimeStamp是做什么的?它的目的是什么?您想要实现什么具体功能?如果您想要一个高质量的答案,您需要提供这些信息。 - cadrell0
最好使用计时器来完成这个任务。 - Aristos
@cadrell0 我想要从 Hub 外部通过 Hub 进行广播 https://github.com/SignalR/SignalR/wiki/Hubs - Registered User
3
如果您能够回答自己的问题,请将其发布为答案并标记为已接受。 - cadrell0
5个回答

15
ASP.NET不适合处理此类要求。如果您需要持续运行的内容,最好创建一个Windows服务。
更新
ASP.NET不适用于长时间运行的任务。它旨在快速响应HTTP请求。请参见Cyborgx37的答案我可以使用线程在IIS上执行长时间运行的作业吗?以了解一些原因。
更新
现在你终于提到你正在使用SignalR,我看到你正在尝试在ASP.NET中托管SignalR,对吗?我认为你的方法不对,可以看一下项目维基中引用的NuGet包示例。这个示例使用IAsyncHttpHandler来管理任务。

2
第一个链接根本没有提供任何原因,第二个链接只是明显关注服务器可能选择在某些时候回收应用程序池的问题。我还没有听到任何有效的理由,为什么这是一个“坏主意”。将某些内容分解成Windows服务,使您的应用程序更难部署,对我来说似乎是个坏主意。有人有真正的理由吗?还是这只是一种意识形态? - John
...非常明显的问题是服务器可能会选择在某个时候回收应用程序池,这是一个相当大的问题... - jrummell
2
所有应用程序总会在某个时刻崩溃,这不仅适用于ASP.NET。您是否反对在WinForms应用程序中执行后台作业,因为用户可以随时单击关闭按钮?即使是Windows服务,在服务器重启时也需要关闭。这有什么不同吗? - John
3
我并没有争论,只是陈述我的观点:)。我认为这并不是一个平等的比较,因为在表单和服务中,用户的某个操作会关闭应用程序,而IIS应用程序池可能由于多种原因(寿命、请求计数、内存使用等)而被回收,这些原因超出了用户的控制范围。 - jrummell

10

你可以在global.asax中启动一个线程,但它只会运行到asp.net进程被回收。这至少每天会发生一次,或者当没有人使用你的站点时。如果进程被回收,唯一重新启动线程的方式是当你的网站有访问时。所以该线程不会持续运行。

为了获得连续的进程,最好启动一个Windows服务。

如果你使用“内部处理”解决方案,那么它实际上取决于你正在做什么。线程本身不会在内存或死锁方面给你带来任何问题。你应该添加一种机制,在应用程序停止时停止你的线程。否则,重新启动将需要很长时间,因为它将等待你的线程停止。


1
太好了!我对它们被回收没有任何问题。那是其中一个要求。 - Registered User
我同意同行的观点。关于这个问题的解决方案和对其危险性的警告可以在这篇优秀文章中找到:http://haacked.com/archive/2011/10/16/the-dangers-of-implementing-recurring-background-tasks-in-asp-net.aspx/ - Ricardo C

5

2
这取决于你在while循环中试图实现什么,但一般情况下,Windows服务是最佳答案。安装Windows服务需要在Web服务器上具有管理员权限。
使用无限循环会产生许多与Windows消息泵相关的问题。这是使Windows应用程序保持活动状态的东西,即使应用程序没有“做”任何事情。如果没有它,程序就会结束。
无限循环的问题在于应用程序被卡住了,“做”某些事情,这会阻止其他应用程序(或线程)“做”它们的事情。虽然有一些解决方法,例如Windows Forms中的DoEvents,但它们在响应性和资源管理方面都存在一些严重的缺点。(在小型LOB应用程序上可以接受,但在Web服务器上可能不行。)即使while循环在单独的线程上,它也会耗尽所有可用的处理能力。
异步编程主要是为长时间运行的进程而设计的,例如等待数据库返回结果或等待打印机上线。在这些情况下,需要等待的是外部进程,而不是 while 循环。
如果无法使用 Window 服务,则我认为最好的方法是设置一个带有自己消息泵的单独线程,但这有点复杂。我从未在 Web 服务器上执行过此操作,但您可能能够启动一个应用程序。这将为您提供一个消息泵,并允许您响应 Windows 事件等。唯一的问题是,这将启动一个 Windows 应用程序(WPFWinForms),这可能不适用于 Web 服务器。
您想要实现什么?有没有其他方式可以完成?

1

小心 - 无限循环会消耗你的处理器。此外,您必须确保不会意外启动这些东西的多个实例(这意味着需要进行一些多线程检查)。 - JDB
@Cyborgx37 嗯,这就是我发布 OP 的原因,但不幸的是在 SO 上噪声太多了… 如果您自己阅读了 OP ,您就会知道该线程将休眠很长时间,所以那里不存在 CPU 问题。 - Registered User

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接