线程计时器不回调

时间:2015-07-18 15:35:44

标签: c# asp.net asp.net-mvc entity-framework

我有几个机器类,它们说明它们是否在线/离线,以及DateTime EndsAt,如果它们在线则会脱机。它们是使用EF(映射?)到数据库。当我打开它们时,我会通过秒数让它们保持联机并创建System.Threading.Timer,以便在时机到来时将其状态更改为离线(EndsAt == DateTime.Now)。打开它们工作正常,但它们不会关闭 - 从不调用turnoff()。最重要的是,如果它被调用,对象将改变它自己的变量,它们是否会被实体框架保存?

public class Machine
{
    private Timer timer=null;
    [Key]
    public int MachineId { get; set; }
    public bool Online { get; set; }
    public DateTime EndsAt { get; set; }

    public void TurnOn(TimeSpan amount)
    {
        Debug.WriteLine("Turn on reached");
        if (!Online)
        {
            EndsAt = DateTime.Today.Add(amount);
            Online = true;
            setTimer();
        }
    }

    private void turnOff(object state)
    {
        Online = false;
        Occuppied = false;
        Debug.WriteLine("Timer ended!");
    }

    private void setTimer()
    {
        Debug.WriteLine("Timer being set");
        if (EndsAt.CompareTo(DateTime.Now) == 1)
        {
            timer = new Timer(new TimerCallback(turnOff));
            int msUntilTime = (int)((EndsAt - DateTime.Now).TotalMilliseconds);
            timer.Change(msUntilTime, Timeout.Infinite);
        }
        else
        {
            Debug.WriteLine("EndsAt is smaller than current date");
        }
    }
}

调用turnOn()的控制器方法

 [HttpPost]
    public ActionResult TurnOn() {
        bool isChanged = false;
        if (Request["machineId"] != null && Request["amount"] != null)
        {
            byte machineId = Convert.ToByte(Request["machineId"].ToString());
            int amount = Convert.ToInt32(Request["amount"].ToString());

            foreach (var machine in db.Machines.ToList())
            {
                if (machine.MachineId == machineId)
                {
                    machine.TurnOn(TimeSpan.FromSeconds(amount));
                    db.Entry(machine).State = EntityState.Modified;
                    db.SaveChanges();
                    isChanged = true;
                }
            }
        }
        if (isChanged)
            return new HttpStatusCodeResult(HttpStatusCode.OK);
        else
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }

2 个答案:

答案 0 :(得分:2)

问题不是来自实体框架,而是来自ASP.NET。

我能描述它的最佳方式是想象一下,ASP.NET中的页面请求是一个控制台应用程序,应用程序启动的每个新请求,请求并响应用户,等待一小部分请求来然后退出Main()函数。

如果你在那种应用程序中创建一个Timer,一旦“微小的位”用完并且Main()返回你的计时器就不会再运行了,那你等待发生的事情永远不会发生。 IIS完成了这个过程,但是它通过AppDomain回收来实现,如果没有请求进入,它将关闭AppDomain并将终止你的计时器。

我知道有两种方法可以解决这个问题:

第一种方法是你需要创建一个第二个应用程序,它作为IIS外部的Windows服务运行,它始终在运行,它将保存计时器。当您想要运行任何类型的长时间运行操作时,您将使用WCF或Web应用程序的某些其他技术与服务进行通信以启动计时器,当计时器完成时,服务执行任何操作你想完成。

第二种方法是将计时器请求保存在数据库中,然后在每次请求检查事件数据库之前在后台保存并查看是否需要执行。像hangfire这样的库可以让这个过程变得简单,它们也有一些技巧可以让应用程序域保持更长时间,或者如果它关闭则将其唤醒(通常它们使用两个相互通信的网站,每个网站保持另一个一个人活着)。

答案 1 :(得分:0)

尽管这个具体问题已经得到解答,但我希望这里有一些相关的讨论,如果计时器回调无法正常工作,我希望能有所帮助。

使用Threading.Timer

时的导入注意事项

1。)计时器需要进行垃圾回收。即使是活跃的,如果它没有参考,也可能被收集为垃圾。

2。)DotNet有许多不同类型的计时器,因为它涉及线程,所以正确使用正确的方法非常重要。使用Forms.Timer for Forms,Threading.Timer或将其包装在Timers.Timer(debate on thread safety)中,或者将Web.UI.Timer与ASP.NET一起用于网页回发。

3.。)当实例化定时器并且无法更改时,定义了Callback方法。

计时器相关工具

1。)您可以使用Thread.Sleep释放CPU资源并将线程置于基本停止的等待状态。

2.。)有时候Task可以与定时器一起使用或代替定时器。

3。)Stopwatch可以以不同的方式使用,例如,使用空循环。