2011-09-07 85 views
2

在我的MVC应用程序中,我有一个控制器,它创建一个Model对象的实例。在Model内,我有一个定时器,它每700ms执行一个函数。当我在我的网站中运行控制器时,即使在关闭浏览器窗口后,我仍然可以看到计时器正在运行。 (定时器启动/停止串口连接,并且我有一个指示何时有通信的指示灯)。查看控制器是否仍在使用的MVC代码

通信实际上从来没有停止,直到我完全重新启动IIS。

是否有某种方式可以检查控制器是否仍在使用中,从而在控制器不再处于活动状态时停止计时器?

下面是型号代码:

public CheckPulses(int interval) { 
    CheckPulsesTimer(interval); 
} 

public void CheckPulsesTimer(int interval) { 
    Timer timer = new Timer(); 
    timer.Enabled = true; 
    timer.AutoReset = true; 
    timer.Interval = interval; 
    timer.Elapsed += new ElapsedEventHandler(GetPulses); 
} 

这里是控制器代码:

public ActionResult Index() { 
    CheckPulses Pulses = new CheckPulses(700); 
    return View(); 
} 

我想也许我应该只是添加一些东西到GetPulses事件,该事件发生的每一次计时器间隔到期,并检查控制器是否仍在使用中。如果不是,请停止计时器。但是,我不知道如何写这张支票。任何人都可以将我指向正确的方向吗?

+0

显示您的控制器代码。 –

+5

您启动了一个计时器,但从未停止它。它有没有奇迹它继续运行?它不会因为您关闭浏览器窗口而奇迹般停止。 Web服务器不知道浏览器窗口是否打开。 –

+0

坦率地说,你不应该基于控制器的生命周期来管理定时器的生命周期,因为它的生命周期是框架的实现细节。相反,您需要一些客户端JavaScript,告诉串行端口控制器它仍然活着,并在客户端停止触发事件时拆除串行端口连接。 – Yaur

回答

-3

您可以使用JQuery + MVC停止Timer。

jQuery的可以到页面的卸载响应事件,像这样:

$(window).unload(function() { 
    alert('Handler for .unload() called.'); 
}); 

你可以让你的控制器的AJAX调用。我们还需要创建一个属性来访问定时器。我假设你叫你的计时器物业MyTimer

Timer MyTimer { get; set; } 

所以,你需要创建一个控制器返回void并调用您的Timer的stop()方法。 [HttpPost] public void KillTimer() { MyTimer.Stop(); }

现在,如果您向我们之前创建的卸载事件添加了Ajax调用,那么只要页面关闭,我们就应该可以终止计时器。

$.ajax(
{ 
    type: "POST", 
    url: "/Home/Index", 
    , 
    success: function(result) 
    { 
     alert("Timer Closed"); 
    },     
    error : function(req, status, error) 
    { 
     alert("Sorry! We could not receive your feedback at this time."); 
    } 
}); 

成功和错误功能不是必需的。我只将它们包括在内进行演示。 我也猜想你的控制器的名字是Home。如果它不同,你将需要更新URL。你可以用纯JavaScript来做到这一点。 JQuery有助于避免编写大量繁琐的浏览器兼容性代码。 (我听说歌剧是一个卸载部分的痛苦)

要解决潜在的浏览器崩溃和意外事件的问题,你可以连接你的控制器来检查定时器是否超过了最大时间限制。

祝你好运!

来源:

JQuery Unload()

Invoking ASP.NET MVC Actions from JavaScript using jQuery

JQuery Ajax()

+0

对于这样的系统资源来说,这是一个非常危险的方法。如果用户浏览器崩溃,例如..或他们的互联网退出,他们关闭浏览器..资源不会被释放。它也不适用于不支持JavaScript的浏览器。 –

+0

客户端答案不解决此服务器端问题。 –

+0

这不是2005年。非JavaScript浏览器正在逐渐淡出。网络是一个JavaScript世界。这是我的意见,但支持。 @Chris没有这方面的要求,不是客户端解决方案。 – BentOnCoding

2

你很可能不停止或处置定时器。这将导致它不被垃圾收集,并在应用程序保持运行期间保持活动状态。

您应该停止并在Controller的Dispose方法中处理Timer。

+0

说真的,反对票吗?为什么? –

+0

问题是如何检查控制器是否在使用中,而不是如何停止定时器和调用处置。 – BentOnCoding

+0

虽然它可能是解决我的问题,我的问题的方式..我如何使用处置方法和方法应输入在哪里? –

0

您有几种选择:

  1. 覆盖处置控制器上的方法,并调用Pulses.Dispose()
  2. 覆盖控制器上OnActionExecuted方法处置您的模型,并且配置
  3. 创建自定义动作过滤器并执行OnActionExecuted方法并将其分配给控制器或动作(您可以将对模型的引用放入TempData,然后在ActionFilter中检查是否filterContext.Controller.TempData [ “MyReferenceToModel”]!= NULL,然后用它来配置定时器)
  4. 在模型中实现IDisposable接口,并使用它里面using语句

希望这有助于 迪马

2

我认为这里的核心问题在于您了解控制器在使用中是什么。

看来,如果你想象如下:

  1. 在您的网址,用户类型和浏览器连接到服务器。
  2. 服务器将请求路由到MVC控制器,该控制器保持与浏览器的连接,并使用此连接响应每个后续请求。
  3. 用户关闭他们的浏览器或导航 - 并且控制器关闭连接并处置。

但是,这并不是实际发生的情况。网络流量通常是一系列孤立的,单独的请求/响应配对。浏览器和服务器之间没有正在进行的连接。

更正确的顺序是类似如下:

  1. 用户在你的URL类型,浏览器连接到服务器上的端口80(或https为443),传递一个有效的HTTP请求,包含一个URL,HTTP动词和其他信息。
  2. 您的服务器使用HTTP请求的信息路由请求特定的一段可执行代码(在这种情况下,您的Controller的特定Action方法)。
  3. 为了执行,服务器创建您的Controller的实例,然后使用HTTP请求的信息作为参数来触发它的Action方法。
  4. 您的Controller代码执行,并且HTTP响应消息被发送回浏览器。
  5. Controller.Dispose()'d由ControllerFactory

您遇到的问题不是由于您的Controller“活跃”引起的 - 因为这不是Controller的问题。相反,您在ModelModel之间创建一个Timer,因此,它继续在内存中生存,无法被GarbageCollection杀死。有关更多详情,请参阅this answer

在本质上,认为它是这样的:

Controller充当工厂。当您拨打Index的方法时,这相当于告诉工厂“生产1 Model”。一旦Model退出工厂,工厂可以自由关闭,关掉灯,并将所有工人送回家(Controller.Dispose()'d)。

然而,Model生活在记忆中。在大多数情况下,Model可能会由于GarbageCollection超出范围而死亡 - 但由于其中有一个活动的Timer,因此某些操作会阻止该进程执行 - 因此Model会继续存在于您的服务器内存中,高兴地在每个计时器到期时一次又一次地启动它的代码。

注意,这有什么跟用户是否仍是您的网站,或者他们的浏览器是否仍处于打开状态等,与变量范围和GarbageCollection对自然做你的服务器。现在你已经开始了一个持续的过程,直到被告知停止。

一个潜在的解决方案:

为了解决这个问题,你可以考虑以下几点:

  1. 创建Timer内部的Singleton对象。喜欢的东西:

    public static class ConnectionManager { 
        private static bool initialized = false; 
        private static DateTime lastStartedAt = DateTime.Now; 
    
        private static Timer timer = null; 
    
        private static void Initialize() { 
         if (timer == null) { 
          timer = new Timer() { 
           AutoReset = true, 
          }; 
          timer.Elapsed += new ElapsedEventHandler(GetPulses); 
         } 
        } 
    
        public static void Start(int interval) { 
         lastStartedAt = DateTime.Now; 
         Initialize(); // Create a timer if necessary. 
         timer.Enabled = true; 
         timer.Interval = interval; 
        } 
        public static void Stop() { 
         timer.Enabled = false; 
        } 
    } 
    
  2. 更改Controller到这样的事情:

    public ActionResult Index() { 
        ConnectionManager.Start(700); 
        return View(); 
    } 
    
  3. 当你处理Timer事件到期,请检查在多久以前lastStartedAt事件发生。如果超过X分钟,请勿处理,并启动ConnectionManager.Stop()

这将使您能够保持连续活动轮询滚动到期。换句话说 - 每次用户请求您的页面时,您都将刷新计时器,并确保您正在收听。但在X分钟后,如果没有用户提出请求 - 只需关闭定时器和关联的端口即可。

+1

很好的解释了Web应用程序的非连接性。 – Graham

相关问题