草庐IT

c# - 性能计数器 - System.InvalidOperationException : Category does not exist

coder 2024-05-26 原文

我有以下类,它返回 IIS 每秒的当前请求数。我每分钟调用 RefreshCounters 以保持每秒请求数刷新(因为它是平均值,如果我将它保留太久,旧值会影响结果太多)......当我需要显示当前 RequestsPerSecond 时,我调用该属性。

public class Counters
{
    private static PerformanceCounter pcReqsPerSec;
    private const string counterKey = "Requests_Sec";
    public static object RequestsPerSecond
    {
        get
        {
            lock (counterKey)
            {
                if (pcReqsPerSec != null)
                    return pcReqsPerSec.NextValue().ToString("N2"); // EXCEPTION
                else
                    return "0";
            }
        }
    }

    internal static string RefreshCounters()
    {
        lock (counterKey)
        {
            try
            {
                if (pcReqsPerSec != null)
                {
                    pcReqsPerSec.Dispose();
                    pcReqsPerSec = null;
                }

                pcReqsPerSec = new PerformanceCounter("W3SVC_W3WP", "Requests / Sec", "_Total", true);
                pcReqsPerSec.NextValue();

                PerformanceCounter.CloseSharedResources();

                return null;
            }
            catch (Exception ex)
            {
                return ex.ToString();
            }
        }
    }
}

问题是有时会抛出以下异常:
System.InvalidOperationException: Category does not exist.

at System.Diagnostics.PerformanceCounterLib.GetCategorySample(String machine,\ String category)
at System.Diagnostics.PerformanceCounter.NextSample()
at System.Diagnostics.PerformanceCounter.NextValue()
at BidBop.Admin.PerfCounter.Counters.get_RequestsPerSecond() in [[[pcReqsPerSec.NextValue().ToString("N2");]]]

我没有正确关闭以前的 PerformanceCounter 实例吗?我做错了什么以至于有时会出现异常?

编辑:
并且只是为了记录,我在 IIS 网站中托管这个类(当然,也就是托管在具有管理权限的 App Pool 中)并从 ASMX 服务调用方法。使用计数器值(显示它们)的站点每 1 分钟调用一次 RefreshCounters,每 5 秒调用一次 RequestsPerSecond; RequestPerSecond 在调用之间缓存。

我每 1 分钟调用一次 RefreshCounters ,因为值往往会变得“陈旧” - 太受旧值的影响(例如,实际 1 分钟前)。

最佳答案

Antenka 在这里为您指引了一个好的方向。您不应该在每次更新/请求值(value)时处理和重新创建性能计数器。实例化性能计数器是有成本的,并且第一次读取可能不准确,如下面的引用所示。还有您的 lock() { ... }语句非常广泛(它们涵盖了很多语句)并且速度会很慢。最好让你的锁尽可能小。我正在给 Antenka 投票以获取质量引用和好的建议!

但是,我想我可以为您提供更好的答案。我在监控服务器性能方面有相当多的经验,并且完全了解您的需求。您的代码没有考虑到的一个问题是,任何显示性能计数器的代码(.aspx、.asmx、控制台应用程序、winform 应用程序等)都可能以任何方式请求此统计信息;它可以每 10 秒请求一次,也许每秒 5 次,您不知道也不应该在意。因此,您需要将 PerformanceCounter 收集代码与实际报告当前 Requests/Second 值的代码分开。出于性能原因,我还将向您展示如何在第一次请求时设置性能计数器,然后保持它运行,直到 5 秒内没有人提出任何请求,然后正确关闭/处置 PerformanceCounter。

public class RequestsPerSecondCollector
{
    #region General Declaration
    //Static Stuff for the polling timer
    private static System.Threading.Timer pollingTimer;
    private static int stateCounter = 0;
    private static int lockTimerCounter = 0;

    //Instance Stuff for our performance counter
    private static System.Diagnostics.PerformanceCounter pcReqsPerSec;
    private readonly static object threadLock = new object();
    private static decimal CurrentRequestsPerSecondValue;
    private static int LastRequestTicks;
    #endregion

    #region Singleton Implementation
    /// <summary>
    /// Static members are 'eagerly initialized', that is, 
    /// immediately when class is loaded for the first time.
    /// .NET guarantees thread safety for static initialization.
    /// </summary>
    private static readonly RequestsPerSecondCollector _instance = new RequestsPerSecondCollector();
    #endregion

    #region Constructor/Finalizer
    /// <summary>
    /// Private constructor for static singleton instance construction, you won't be able to instantiate this class outside of itself.
    /// </summary>
    private RequestsPerSecondCollector()
    {
        LastRequestTicks = System.Environment.TickCount;

        // Start things up by making the first request.
        GetRequestsPerSecond();
    }
    #endregion

    #region Getter for current requests per second measure
    public static decimal GetRequestsPerSecond()
    {
        if (pollingTimer == null)
        {
            Console.WriteLine("Starting Poll Timer");

            // Let's check the performance counter every 1 second, and don't do the first time until after 1 second.
            pollingTimer = new System.Threading.Timer(OnTimerCallback, null, 1000, 1000);

            // The first read from a performance counter is notoriously inaccurate, so 
            OnTimerCallback(null);
        }

        LastRequestTicks = System.Environment.TickCount;
        lock (threadLock)
        {
            return CurrentRequestsPerSecondValue;
        }
    }
    #endregion

    #region Polling Timer
    static void OnTimerCallback(object state)
    {
        if (System.Threading.Interlocked.CompareExchange(ref lockTimerCounter, 1, 0) == 0)
        {
            if (pcReqsPerSec == null)
                pcReqsPerSec = new System.Diagnostics.PerformanceCounter("W3SVC_W3WP", "Requests / Sec", "_Total", true);

            if (pcReqsPerSec != null)
            {
                try
                {
                    lock (threadLock)
                    {
                        CurrentRequestsPerSecondValue = Convert.ToDecimal(pcReqsPerSec.NextValue().ToString("N2"));
                    }
                }
                catch (Exception) {
                    // We had problem, just get rid of the performance counter and we'll rebuild it next revision
                    if (pcReqsPerSec != null)
                    {
                        pcReqsPerSec.Close();
                        pcReqsPerSec.Dispose();
                        pcReqsPerSec = null;
                    }
                }
            }

            stateCounter++;

            //Check every 5 seconds or so if anybody is still monitoring the server PerformanceCounter, if not shut down our PerformanceCounter
            if (stateCounter % 5 == 0)
            {
                if (System.Environment.TickCount - LastRequestTicks > 5000)
                {
                    Console.WriteLine("Stopping Poll Timer");

                    pollingTimer.Dispose();
                    pollingTimer = null;

                    if (pcReqsPerSec != null)
                    {
                        pcReqsPerSec.Close();
                        pcReqsPerSec.Dispose();
                        pcReqsPerSec = null;
                    }
                }                                                      
            }

            System.Threading.Interlocked.Add(ref lockTimerCounter, -1);
        }
    }
    #endregion
}

好的,现在解释一下。
  • 首先你会注意到这个类被设计成一个静态的单例。
    你不能加载它的多个副本,它有一个私有(private)构造函数
    并且急切地初始化了自身的内部实例。这使得
    确保您不会意外创建相同的多个副本PerformanceCounter .
  • 接下来你会注意到私有(private)构造函数(这只会运行
    一旦第一次访问类时)我们创建两个PerformanceCounter和一个计时器,用于轮询PerformanceCounter .
  • Timer 的回调方法将创建 PerformanceCounter如果
    需要并获取其下一个值可用。也是每 5 次迭代
    我们将查看自您上次请求PerformanceCounter的值(value)。如果超过 5 秒,我们将
    关闭轮询计时器,因为它目前不需要。我们可以
    如果我们再次需要它,请稍后再启动它。
  • 现在我们有一个名为 GetRequestsPerSecond() 的静态方法为你
    将返回 RequestsPerSecond 的当前值的调用PerformanceCounter .

  • 这种实现的好处是你只创建一次性能计数器,然后继续使用直到你完成它。它易于使用,因为您只需拨打 RequestsPerSecondCollector.GetRequestsPerSecond()从任何你需要的地方(.aspx、.asmx、控制台应用程序、winforms 应用程序等)。永远只有一个 PerformanceCounter并且无论您拨打 RequestsPerSecondCollector.GetRequestsPerSecond() 的速度有多快,它都会以每秒精确的速度轮询 1 次。 .它还会自动关闭并处理 PerformanceCounter如果您在 5 秒内没有请求它的值。当然,您可以调整计时器间隔和超时毫秒以满足您的需要。你可以在 60 秒而不是 5 秒内更快地轮询和超时。我选择了 5 秒,因为它证明它在 Visual Studio 中调试时工作得非常快。一旦您测试它并知道它可以工作,您可能需要更长的超时时间。

    希望这不仅可以帮助您更好地使用 PerformanceCounters,而且可以安全地重用这个类,它与您想要显示统计信息的任何东西都是分开的。可重用的代码总是一个加分项!

    编辑:作为后续问题,如果您想在此性能计数器运行时每 60 秒执行一次清理或保姆任务,该怎么办?好吧,我们已经让计时器每 1 秒运行一次,并且有一个变量跟踪我们的循环迭代,称为 stateCounter每个计时器回调都会增加。所以你可以添加一些这样的代码:
    // Every 60 seconds I want to close/dispose my PerformanceCounter
    if (stateCounter % 60 == 0)
    {
        if (pcReqsPerSec != null)
        {
            pcReqsPerSec.Close();
            pcReqsPerSec.Dispose();
            pcReqsPerSec = null;
        }
    }
    

    我应该指出示例中的这个性能计数器不应该“过时”。我相信“请求/秒”应该是平均值而不是移动平均统计数据。但是这个示例只是说明了您 可以 定期对您的 PerformanceCounter 进行任何类型的清理或“照看”的方式时间间隔。在这种情况下,我们正在关闭并处理性能计数器,这将导致它在下一个计时器回调时重新创建。您可以根据您的用例和您正在使用的特定 PerformanceCounter 修改它。大多数人阅读这个问题/答案应该不需要这样做。检查您想要的 PerformanceCounter 的文档,看看它是否是连续计数、平均值、移动平均值等......并适当调整您的实现。

    关于c# - 性能计数器 - System.InvalidOperationException : Category does not exist,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8171865/

    有关c# - 性能计数器 - System.InvalidOperationException : Category does not exist的更多相关文章

    1. c# - 如何在 ruby​​ 中调用 C# dll? - 2

      如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

    2. C# 到 Ruby sha1 base64 编码 - 2

      我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

    3. ruby-on-rails - Ruby on Rails 计数器缓存错误 - 2

      尝试在我的RoR应用程序中实现计数器缓存列时出现错误Unknownkey(s):counter_cache。我在这个问题中实现了模型关联:Modelassociationquestion这是我的迁移:classAddVideoVotesCountToVideos0Video.reset_column_informationVideo.find(:all).eachdo|p|p.update_attributes:videos_votes_count,p.video_votes.lengthendenddefself.downremove_column:videos,:video_vot

    4. 基于C#实现简易绘图工具【100010177】 - 2

      C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

    5. Ruby 的数字方法性能 - 2

      我正在使用Ruby解决一些ProjectEuler问题,特别是这里我要讨论的问题25(Fibonacci数列中包含1000位数字的第一项的索引是多少?)。起初,我使用的是Ruby2.2.3,我将问题编码为:number=3a=1b=2whileb.to_s.length但后来我发现2.4.2版本有一个名为digits的方法,这正是我需要的。我转换为代码:whileb.digits.length当我比较这两种方法时,digits慢得多。时间./025/problem025.rb0.13s用户0.02s系统80%cpu0.190总计./025/problem025.rb2.19s用户0.0

    6. ruby - Ruby 性能中的计时器 - 2

      我正在寻找一个用ruby​​演示计时器的在线示例,并发现了下面的代码。它按预期工作,但这个简单的程序使用30Mo内存(如Windows任务管理器中所示)和太多CPU有意义吗?非常感谢deftime_blockstart_time=Time.nowThread.new{yield}Time.now-start_timeenddefrepeat_every(seconds)whiletruedotime_spent=time_block{yield}#Tohandle-vesleepinteravalsleep(seconds-time_spent)iftime_spent

    7. ruby-on-rails - 如果条件与 &&,是否有任何性能提升 - 2

      如果用户是所有者,我有一个条件来检查说删除和文章。delete_articleifuser.owner?另一种方式是user.owner?&&delete_article选择它有什么好处还是它只是一种写作风格 最佳答案 性能不太可能成为该声明的问题。第一个要好得多-它更容易阅读。您future的自己和其他将开始编写代码的人会为此感谢您。 关于ruby-on-rails-如果条件与&&,是否有任何性能提升,我们在StackOverflow上找到一个类似的问题:

    8. c# - C# 中的 Flatten Ruby 方法 - 2

      我如何做Ruby方法"Flatten"RubyMethod在C#中。此方法将锯齿状数组展平为一维数组。例如:s=[1,2,3]#=>[1,2,3]t=[4,5,6,[7,8]]#=>[4,5,6,[7,8]]a=[s,t,9,10]#=>[[1,2,3],[4,5,6,[7,8]],9,10]a.flatten#=>[1,2,3,4,5,6,7,8,9,10 最佳答案 递归解决方案:IEnumerableFlatten(IEnumerablearray){foreach(variteminarray){if(itemisIEnume

    9. ruby - 如何找到我的 Ruby 应用程序中的性能瓶颈? - 2

      我编写了一个Ruby应用程序,它可以解析来自不同格式html、xml和csv文件的源中的大量数据。我如何找出代码的哪些区域花费的时间最长?有没有关于如何提高Ruby应用程序性能的好资源?或者您是否有任何始终遵循的性能编码标准?例如,你总是用加入你的字符串吗?output=String.newoutput或者你会使用output="#{part_one}#{part_two}\n" 最佳答案 好吧,有一些众所周知的做法,例如字符串连接比“#{value}”慢得多,但是为了找出您的脚本在哪里消耗了大部分时间或比所需时间更多,您需要进行分

    10. ruby - 可以像在 C# 中使用#region 一样在 Ruby 中使用 begin/end 吗? - 2

      我最近从C#转向了Ruby,我发现自己无法制作可折叠的标记代码区域。我只是想到做这种事情应该没问题:classExamplebegin#agroupofmethodsdefmethod1..enddefmethod2..endenddefmethod3..endend...但是这样做真的可以吗?method1和method2最终与method3是同一种东西吗?还是有一些我还没有见过的用于执行此操作的Ruby惯用语? 最佳答案 正如其他人所说,这不会改变方法定义。但是,如果要标记方法组,为什么不使用Ruby语义来标记它们呢?您可以使用

    随机推荐