在预定义的时间有效地运行数十万个函数

时间:2012-12-31 18:54:26

标签: c# .net multithreading optimization

我需要在预定义的时间以有效的方式运行数十万个函数,

我目前的代码是这样的:

class myclass
{
    public DateTime NextTime = DateTime.Now;
    Random rand = new Random();

    public void DoStuff()
    {
        if (NeedToWork())
        {
            // do some complex stuff on a 2nd thread.
            NextTime = DateTime.Now.AddSeconds(rand.Next(60, 3600));
        }
    }

    public bool NeedToWork()
    {
        return DateTime.Now > NextTime;
    }

}

从计时器运行的调用函数:

    static List<myclass> mylist = new List<myclass>();
    static void Activator()
    {
        foreach (var item in mylist)
        {
            item.DoStuff();
        }

    }

我的问题是当集合中有大量项目时,所有这些项目需要花费很长时间,导致一些DoStuff()函数在某些情况下运行时间超过一分钟。

目前,同时从不同的线程调用“Activator”函数以使延迟时间尽可能低(使用Mutex来处理必要的线程同步)

我想到的两个解决方案:

  1. 而不是一个List<myclass>,我可以有一个像Dictionary<DateTime, List<myclass>>这样的字典,精度为1秒,每秒运行相应的类对象,dictionay将映射'nexttime'到'myclass'实例。
  2. 创建两个List<> s或Queue<> s而不是一个列表,它们将被命名为'fastqueue','slowqueue',slowqueue将拥有所有对象,fastqueue将拥有所有项目很快就需要工作,然后有一个专用线程循环通过慢队列,并检查剩余时间并将其放入快速队列。
  3. 注意:

    1. 实际代码没有任何随机数据确定下一个运行时间,它实际上是基于某些计算,这只是一个样本。

    2. 每个项目运行所需的时间不会超过一分之一秒,并且每个项目在一小时内最多只运行4次。 ram和cpu power不是问题,我已经测试并在不同的区域进行了大量的优化以使其适合,尽管这里没有显示所有代码。

    3. 唯一浪费cpu时间的是返回DateTime.Now&gt;的行。 NextTime

4 个答案:

答案 0 :(得分:3)

我无法保证它能解决您的问题,因为我不知道您有多少计算能力,但您是否尝试过Parallel.ForEach而不是从不同的线程调用Activator?你可以这样做。

Parallel.ForEach(mylist, item =>
{
    item.DoStuff();
});

如果要限制运行的并发线程数,可能还需要在调用MaxDegreeOfParallelism时设置Parallel.ForEach。如果我的答案不清楚或不够详细,请发表评论。

编辑:正如评论所述,我的示例中的DoStuff()将同步执行。使用Task.Factory.StartNew()或任何等价物来利用任务调度程序可能会有所帮助。然而,作者指出,大多数任务都很小,并且在很短的时间内执行。出于这个原因,我认为实际的调度会导致不必要的开销,而不是在不同的线程上执行串行执行。

答案 1 :(得分:3)

您可能希望维护已排序的队列或工作树,并按运行时间排序。

然后你的常规间隔定时器循环运行队列中的前N个项目,当项目超出当前间隔时间时停止,因为不需要进一步查看。

在生成要添加到队列的新工作时,使用已排序的数据结构,插入时应正确放置以保持排序。

(在添加和删除队列时,您还需要担心线程安全。)

(仅供参考,调度算法有很多变化。)


另外请注意,虽然不确定它是否对您有用,但是在使用这些基于时间的公式时,您可能会考虑使用DateTime.Now快照多次使用,否则您可以“松散的“如果此线程在调用DateTime.Now

之间中断的时间
public void DoStuff(DateTime now)
{
    if (NeedToWork(now))
    {
        // do some complex stuff on a 2nd thread.
        NextTime = now.AddSeconds(rand.Next(60, 3600));
    }
}

答案 2 :(得分:1)

增量队列 - 按超时时间排序的列表,是处理大量长超时的常用方法。我使用一个线程来管理列表。它等待输入的BlockingCollection队列,其超时设置为now和介于列表头部的项目的超时时间之间的时间间隔。如果等待超时,它会弹出并触发队列头部的项目,获取新的头部对象,重新计算其等待时间并再次等待输入队列(如果列表为空,则超时设置为INFINITE)。新的超时项被推送到输入队列,并且线程在恢复其超时活动之前插入它们。

答案 3 :(得分:0)

这取决于您尝试执行的任务的复杂程度以及您拥有的硬件资源量,但我建议使用库来完成此类工作:Quartz.net可能很有用。