2

我需要strategy.AllTablesUpdated();在 2 毫秒内执行 50 个策略(我需要每秒重复约 500 次)。使用下面的代码,我发现仅Monitor.TryEnter调用最多花费1 毫秒(!!!),我这样做了 50 次!

    // must be called ~500 times per second
    public void FinishUpdatingTables()
    {
        foreach (Strategy strategy in strategies)   // about ~50, should be executed in 2 ms
        {
            // this slow and can be paralleled
            strategy.AllTablesUpdated();
        }
    }

.....................

    public override bool AllTablesUpdated(Stopwatch sw)
    {
        this.sw = sw;
        Checkpoint(this + " TryEnter attempt ");
        if (Monitor.TryEnter(desiredOrdersBuy))
        {
            Checkpoint(this + " TryEnter success ");
            try
            {
                OnAllTablesUpdated();
            } finally
            {
                Monitor.Exit(desiredOrdersBuy);
            }
            return true;
        } else
        {
            Checkpoint(this + " TryEnter failed ");
        }
        return false;
    }

    public void Checkpoint(string message)
    {
        if (sw == null)
        {
            return;
        }
        long time = sw.ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
        Log.Push(LogItemType.Debug, message + time);
    }

从日志中(以 µs 为单位),失败的尝试花费了大约 1 毫秒:

12:55:43:778 调试:TryEnter 尝试 1264 12:55:43:779 调试:TryEnter 失败 2123

从日志(以 µs 为单位),成功尝试花费了 ~ 0.01ms:

12:55:49:701 调试:TryEnter 尝试 889 12:55:49:701 调试:TryEnter 成功 900

所以现在我认为这Monitor.TryEnter对我来说太昂贵了,不能一个一个地执行 50 个策略。所以我想用Task这样的方式并行这项工作:

    // must be called ~500 times per second
    public void FinishUpdatingTables()
    {
        foreach (Strategy strategy in strategies)  // about ~50, should be executed in 2 ms
        {
            // this slow and can be paralleled
            Task.Factory.StartNew(() => {
                strategy.AllTablesUpdated();
            });
        }
    }

我也可能会替换Monitor.TryEnter为,lock就像使用这种方法一样,一切都将是异步的。

我的问题:

  • 为什么Monitor.TryEnter这么慢?(如果未获得锁定则为 1 ms)
  • Task每 2 毫秒启动 50 个任务 = 每秒 25 000 个任务会有多好?.NET 可以有效地管理这个吗?我还可以使用生产者-消费者模式BlockingCollection并仅启动 50 个“工人”一次,然后每 2 毫秒向 BlockingCollection 提交 50 个项目的新包?那会更好吗?
  • 您将如何执行每 2 毫秒(每秒 500 次)可以并行执行的 50 个方法,总共每秒 25 000 次?
4

1 回答 1

4
  1. Monitor.TryEnter(object) 只是 Monitor.TryEnter(object, 0, ref false) (0 毫秒超时)。如果没有获得锁,那 1 毫秒只是尝试获得锁的开销。
  2. 您可以根据需要启动任意数量的任务,它们都使用 ThreadPool,尽管这将限制为最大线程数。最大值取决于您的系统、内核数量、内存等……但可以肯定的是,它不会是 25,000 个线程。但是,如果您开始干预 TPL 调度程序,您会遇到麻烦。我只是使用Parallel.Foreach,看看它能让我走多远。
  3. Parallel.ForEach. 我还要确保它strategies是类型的IList,以便在不等待迭代器的情况下触发尽可能多的项目。

您尚未将代码粘贴到 OnAllTablesUpdated(),您在该过程期间保持锁定。这将成为你所有可能的瓶颈。

一些问题,你为什么要在表准备好处理时使用锁?

  1. 代表不可以吗?
  2. 为什么在运行策略时锁定它?您是否在每个策略中修改该表?如果是这种情况,你能不把它复印吗?
于 2012-04-18T09:48:32.347 回答