【问题标题】:best way to count how many times per second the method is called计算每秒调用该方法多少次的最佳方法
【发布时间】:2012-01-16 22:53:55
【问题描述】:

我有应该是“QoSed”的 dll 方法——这个方法每秒最多应该被调用 100 次。:

    private static extern int ExecTrans(int connectionId);

这个方法只用在程序中的一个地方,所以qos这个地方是可以的。 我需要为每个connectionId 提供单独的“qos 计数器”。所以ExecTrans(1)ExecTrans(2) 应该去不同的柜台。

在第一次迭代时,我想计算调用方法的频率(对于每个 connectionId)。 IE。我想要“实时统计”。有两种方法:

- allow to exceed limitiation for a short period. for example allow "100 transaction from 0 to 1 second, 100 transaction from 1 to 2 seconds and 200 transactions from 0.5 to 1.5 second".
- at any second interval transactions should not exceed 100.

现在我不在乎使用哪种方法,但我会选择一种减少“开销”的方法。我希望 qos 添加尽可能少的“额外工作”,因为它是对每 0.1 毫秒敏感的交易软件。

至于第一种方法,我想我可以使用类似的东西(伪代码,可能 statscurStats 应该是线程安全的):

private int[] stats      // statistic to display to user
private int[] curStats;  // statistic currently collection

OnOneSecondElapsed(object source, ElapsedEventArgs args) {
    foreach (conId : connIds) {
        stats[conId] = curStats[conId];
        curStats[conId] = 0;
    }
}

myMethod {
    ......
    ExecTrans(conId);
    ++curStats[conId];
    ......
}

至于第二种方法....是否可以创建一个集合,其中对象的生命恰好是一秒钟,一秒钟后消失?然后每次我都会将下一个对象添加到集合中,除非集合包含 100 个对象。

你怎么看?我不熟悉 C# 库文件,所以可能我缺少一些有用的类,也许你可以建议另一种方法。

【问题讨论】:

    标签: c#


    【解决方案1】:

    如果有人需要测量而不是限制...这是一种简单的方法,可以为您提供粗略的估计:

    class A{
        private int _calls;
        private Stopwatch _sw;
    
        public A(){
           _calls = 0;
           _sw = new Stopwatch();
           _sw.Start();
        }
    
        public void MethodToMeasure(){
            //Do stuff
            _calls++;
            if(sw.ElapsedMilliseconds > 1000){
                _sw.Stop();
                //Save or print _calls here before it's zeroed
                _calls = 0;
                _sw.Restart();
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      在某些情况下,您每秒会被调用超过 n 次,我假设您只是不想在额外的情况下进行任何实际处理。

      您可以使用同步的 Q 对象来保存要为每个连接处理的事务。调用您的方法只会将数据排入队列,说明应该做什么。然后,在单独的处理线程中(系统或每个连接只有一个),您可以将操作出列并以每 0.01 秒 1 的速率处理它们。只需将 Q 大小截断为 100(弹出到 100),然后为给定的连接和每个工作单元排队,瞧,您丢弃了额外的工作项。

      注意:您需要一个精确的计时函数来强制每 0.01 秒执行 1 次事务。 例如:

      Stopwatch watch = new Stopwatch();
      int nextPause = watch.Elapsed.Milliseconds + 10;
      while (true)
      {
          //do work (dequeue one item and process it)
      
          int now = watch.Elapsed.Milliseconds;
          if( now < nextPause ) {
              Thread.Sleep( nextPause - now );
          }
          nextPause = watch.Elapsed.Milliseconds + 10;
      }
      

      注意:如果事务花费的时间超过 10 毫秒(1/100 秒),您可能会丢弃额外的工作项...

      如果您希望工作线程更加“突发”,您可以在一个循环中处理多个工作项并使用更长的等待时间,这将需要部分等待以及部分“剩余项目”计数。 (另外,最好使用 Monitor.Pulse 和 Montior.Wait 而不是 sleep...)

      【讨论】:

        【解决方案3】:

        第一种方法:

        • 使用ConcurrentQueue&lt;DateTime&gt;
        • 在每次请求之前,检查队列的大小。如果 > 100,则取消请求
        • 如果
        • 在后台线程中,每 0.1 秒删除超过 1 秒的条目

        它应该是相当有效的,但是:

        • 由于在检查队列计数和入队时间之间没有锁定,因此有时每秒可能会收到略多于 100 个请求
        • 由于后台线程每0.1秒执行一次,如果同时收到100个请求,它可能会阻塞队列长达1.1秒。根据需要调整睡眠时间。

        我可能错了,但我认为没有完美的解决方案。基本上,系统越精确,开销就越大。您必须根据需要调整参数。

        【讨论】:

        • 我猜想达到极限时该怎么做有些混乱。删除新的工作项?删除旧的工作项(如果它们没有内联发生)?引入暂停?
        • 那么可能在计数大于100的时候加一个自旋锁重新检查。
        • 为什么需要“后台线程”?我可以清理每个请求的查询然后我不需要ConcurrentQueue
        • 这只是减少开销的一种方式。
        【解决方案4】:

        有一个称为分析器的工具可以完全满足您的需求。你用你的代码运行它,它会准确地告诉你每个方法花费了多少时间以及每个方法被调用了多少次。这是关于 C# 分析器的old thread。如果您是专业开发人员,您可能已经拥有剖析器的公司许可证。

        【讨论】:

        • 我以后需要QOS!如果某些方法调用的频率高于某个值,我需要引入延迟。而且我不想使用分析器,因为它太慢了。 Profiler 非常适合调试,但我需要在生产中使用此功能。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-02-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多