实现抢占等待队列的最有效方法?

时间:2009-07-24 21:51:28

标签: c# scheduling scheduled-tasks

我一直绞尽脑汁试图解决这个问题。这是场景。我基本上有一个排序的静态列表,其中包含事件应该发生的各种时间。对于可视化:

+-----------------------+
|  Time  |  LastUpdate  |
|-----------------------|
|    1   |   03:10:00   | 0
|    2   |   03:10:00   | 1
|    2   |   03:10:00   | 2
|    3   |   03:10:00   | 3
|    3   |   03:10:00   | 4
|    4   |   03:10:00   | 5
+-----------------------+

因此,第一次通过该方法时,lastTime属性将为null,因此它将“做一些工作”并将lastTime属性设置为当前时间。时间属性表示何时需要再次执行该项目。例如,由于元素0的lastTime03:10:00且时间为1,因此需要在03:11:00执行,元素1和2都有lastTime 03:10:00并且都需要在03:12:00处执行,等等。

以下是我的目标的粗略实现:

public static IList<Item> _list;

public void DoSomething()
{
    while (true)
    {
        for (int i = 0; i < _list.Count; i++)
        {
            var item = new Item();

            if (DateTime.MinValue.Equals(_list[i].LastUpdate))
            {
                item = DoWork(_list[i].Url);
                _list[i].LastUpdate = item.LastUpdate;
                Console.WriteLine(item.Title + " @ " + item.LastUpdate + "; i = " + i);
            }
            else
            {
                var timeToSleep = ((_list[i].LastUpdate.AddMinutes(_list[i].Time)).Subtract(DateTime.Now));

                if (timeToSleep.TotalMilliseconds > 0)
                {
                    for (int j = 0; j < i; j++)
                    {
                        var lastRet = _list[j].LastUpdate.AddMinutes(_list[j].Time);
                        var nextFetch = DateTime.Now.Add(timeToSleep);

                        if (lastRet < nextFetch)
                        {
                            item = DoWork(_list[i].Url);
                            _list[i].LastUpdate = item.LastUpdate;
                            Console.WriteLine(item.Title + " @ " + item.LastUpdate + "; i = " + i);
                        }
                    }
                }

                if (timeToSleep.TotalMilliseconds > 0)
                {
                    Console.WriteLine("Sleeping until: " + DateTime.Now.Add(timeToSleep));
                    System.Threading.Thread.Sleep(timeToSleep);
                }

                item = DoWork(_list[i].Url);
                _list[i].LastUpdate = item.LastUpdate;
                Console.WriteLine(item.Title + " @ " + item.LastUpdate + "; i = " + i);
            }
        }

        Console.WriteLine("--------------------------");
    }
}

如果不需要做任何事情,它将一直睡眠,直到列表中的下一个项目准备好进行更新。内部for循环被放置到位,以防止更频繁更新的项目必须等到更新频率较低的项目再次更新之前。在理想情况下,它会在调用Sleep之前检查它上面的任何项是否需要更新。如果当前项目之上的任何项目在当前项目休眠之前需要更新,则继续并更新它们。如果没有,那么当前项目将调用Sleep等待它准备好更新。我希望这是有道理的。

我是否完全错了?有更简单的解决方案吗?我愿意接受任何建议。此外,请记住,此列表可能会增长到数千个项目。提前谢谢。

1 个答案:

答案 0 :(得分:1)

我不完全理解你的问题描述,但这似乎对我来说不必要的复杂。怎么样:

public static IList<Item> _list;

public void DoSomething()
{
    while (true)
    {
        DateTime minDate = DateTime.MaxValue;

        for (int i = 0; i < _list.Count; i++)
        {
            DateTime nextExecution = _list[i].LastUpdate.AddMinutes(_list[i].Time);

            if (nextExecution <= DateTime.Now)
            {
                var item = DoWork(_list[i].Url);
                _list[i].LastUpdate = item.LastUpdate;
                nextExecution = _list[i].LastUpdate.AddMinutes(_list[i].Time);
                Console.WriteLine(item.Title + " @ " + item.LastUpdate + "; i = " + i);
            }

            if (nextExecution < minDate)
                minDate = nextExecution;
        }

        TimeSpan timeToSleep = minDate.Subtract(DateTime.Now));

        if (timeToSleep.TotalMilliseconds > 0)
        {
            Console.WriteLine("Sleeping until: " + minDate);
            System.Threading.Thread.Sleep(timeToSleep);
        }
    }
}

如果任务数量变大,您可能希望保留按下一个计算执行时间排序的链接列表。这样,您不必每次迭代都遍历整个列表。