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

发布于 2024-07-29 13:28:40 字数 2845 浏览 3 评论 0原文

我绞尽脑汁想弄清楚这个问题。 这是场景。 我本质上有一个排序的静态列表,其中包含事件应该发生的各种时间。 为了可视化:

+-----------------------+
|  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 属性设置为当前时间。 time 属性表示何时需要再次执行该项目。 例如,由于元素 0 的 lastTime03:10:00 且 Time 为 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等待,直到准备好更新。 我希望这是有道理的。

我的做法完全错误吗? 有没有更简单的解决方案? 我对任何建议持开放态度。 另外,请记住,此列表可能会增长到数千个项目。 提前致谢。

I've been racking my brain trying to figure this out. Here is the scenario. I essentially have a sorted static list that contains various times that an event is supposed to happen. For visualization:

+-----------------------+
|  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
+-----------------------+

So, the first time through the method, the lastTime property is going to be null, so it will "do some work" and set the lastTime property to the current time. The time property signifies when the item will need to be executed again. For instance, since element 0 has a lastTime of 03:10:00 and a Time of 1, it will need to be executed at 03:11:00, element 1 and 2 both have a lastTime of 03:10:00 and both need to be executed at 03:12:00, and so and so forth.

Here is a rough implementation of what I have going:

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("--------------------------");
    }
}

If there is nothing that needs to be done, it will sleep until the next item in the list is ready to be updated. The inner for loop is put in place to prevent the more frequently updated items from having to wait until the less frequently items are updated before it can update itself again. In an ideal scenario, it will check to see if any items above it need updating before it calls Sleep. If any items above the current item will need updating before the the current item sleeps, then go ahead and update them. If not, then the current item will go call Sleep to wait until it is ready to be updated. I hope this makes sense.

Am I going about this totally wrong? Is there an easier solution to this? I am open to any and all suggestions. Also, please keep in mind that this list could potentially grow to thousands of items. Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

书间行客 2024-08-05 13:28:41

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

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);
        }
    }
}

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

I don't entirely understand your problem description, but this seems unnecessarily complicated to me. How about:

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);
        }
    }
}

If the number of tasks becomes large, you might want to keep a linked list that is ordered by the next calculated execution time. That way, you don't have to loop through the entire list every iteration.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文