【问题标题】:Why am I getting INSANELY incorrect values for the run duration of a process?为什么我在进程的运行持续时间中得到非常不正确的值?
【发布时间】:2023-03-12 10:43:01
【问题描述】:

基本上,我想为外部进程的运行时间计时不是由我的代码触发的。为了实现这一点,我订阅了特定进程开始和结束的事件进程名称,使用以下代码(基于此answer):

private ManagementEventWatcher ProcessStartWatcher(string processName)
{
    string queryString =
        "SELECT TargetInstance" +
        "  FROM __InstanceCreationEvent " +
        "WITHIN  1 " +
        " WHERE TargetInstance ISA 'Win32_Process' " +
        "   AND TargetInstance.Name = '" + processName + "'";

    // The dot in the scope means use the current machine
    string scope = @"\\.\root\CIMV2";

    // Create a watcher and listen for events
    ManagementEventWatcher watcher = new ManagementEventWatcher(scope, queryString);
    watcher.EventArrived += ProcessStarted;
    watcher.Start();
    return watcher;
}

private ManagementEventWatcher ProcessEndWatcher(string processName)
{
    string queryString =
        "SELECT TargetInstance" +
        "  FROM __InstanceDeletionEvent " +
        "WITHIN  1 " +
        " WHERE TargetInstance ISA 'Win32_Process' " +
        "   AND TargetInstance.Name = '" + processName + "'";

    // The dot in the scope means use the current machine
    string scope = @"\\.\root\CIMV2";

    // Create a watcher and listen for events
    ManagementEventWatcher watcher = new ManagementEventWatcher(scope, queryString);
    watcher.EventArrived += ProcessEnded;
    watcher.Start();
    return watcher;
}

private void ProcessStarted(object sender, EventArrivedEventArgs e)
{
    this.processStart = DateTime.Now;
}

private void ProcessEnded(object sender, EventArrivedEventArgs e)
{
    // use time recorded from ProcessStarted to calculate run duration, write to log file
    this.logger.addRuntimeData((DateTime.Now - this.processStart).TotalSeconds);
}

addRuntimeData(double seconds) 方法定义为:

public void addRuntimeData(double seconds)
{
    this.runDurations.Add(seconds);
    if (this.runDurations.Count > Properties.Settings.Default.MaxRunDurationData)
        this.runDurations.RemoveAt(0);
    this.updateLog();
}

public void updateLog()
{
    this.logfileDirectory = Properties.Settings.Default.LogfileDirectory;
    this.logfileFullPath = logfileDirectory + this.task.Name.toValidFilename() + this.logfileExtension;
    Directory.CreateDirectory(logfileDirectory); // create directory if it does not already exist
    this.toXElement().Save(this.logfileFullPath); // generate the XML and write it to the log file
}

现在,我只是在尝试为我编写的测试过程计时,所有测试过程所做的只是对Console.WriteLine("Test process"); 的一次调用,因此记录持续时间的适当值应大致在 0-2 秒范围内.

有时, 我得到的值是适当的,有时我得到的值实际上是不可能被记录下来的,例如 63619141321.2978 秒。这大约等于 2017.3 年,这让我认为它可能与开始时间被记录为 01/01/0001 00:00:01 或类似的东西有关;这个问题有可能与此有关吗?

如果这是一个我无能为力的问题(例如,如果它与操作系统触发消息的方式/时间有关),有没有办法可以过滤掉这些明显无效的数据积分?

【问题讨论】:

  • Bobby Tables 或许能告诉你。
  • @MattRowland lol rekt savage
  • 可能是因为在ProcessStarted 被触发并初始化processStart 之前记录了一个持续时间。
  • 这里很可能结束事件出现在开始事件之前。观察者只是在投票。 “WITHIN 1”子句的目的是设置轮询间隔。有时,最终进程观察者设法首先捕获其事件。如果开始时间没有初始化,那么它会给你你所看到的。
  • @mikez 那么在ProcessEndWatcher 中将WITHIN 1 更改为WITHIN 2 可以解决此问题,因为由于ProcessStartWatcher 设置为WITHIN 1,这将确保结束事件不是在开始事件之前触发?

标签: c# validation process filtering


【解决方案1】:

根本问题是 ManagementEventWatcher 从根本上说是一种轮询机制,并且结束事件可能会在一个非常短的进程的开始事件之前被拾取。如果开始时间从未被初始化(默认值为 1/1/0001),那么您将看到您所描述的内容。实际上,这可能不是一个现实的用例,但正如您所观察到的那样,它可能会发生。我认为修复它的最佳方法是取消记录流程启动事件的开始时间。

您实际上不需要查看进程启动事件来计算总运行时间。您可以从结束事件中获取 Win32_Process 实例,并使用 CreationDate 来计算进程的总运行时间。我注意到尚未设置 TerminationDate。在这种情况下,我只使用触发结束事件的当前时间:

private static void ProcessEnded(object sender, EventArrivedEventArgs e)
{
    Console.WriteLine($"Process ended event at: {DateTime.Now}");
    var targetProcess = e.NewEvent.Properties["TargetInstance"].Value as ManagementBaseObject;
    if (targetProcess != null)
    {
        Console.WriteLine("Properties:");
        foreach (PropertyData data in targetProcess.Properties)
        {
            Console.WriteLine($"{data.Name} = {data.Value}");
        }

        DateTime creationDate = GetDateTimeOrDefault(targetProcess.Properties["CreationDate"], DateTime.Now);
        DateTime terminationDate = GetDateTimeOrDefault(targetProcess.Properties["TerminationDate"], DateTime.Now);

        var totalRunTime = (terminationDate - creationDate).TotalSeconds;
        Console.WriteLine($"Creation: {creationDate}, Termination: {terminationDate}, Elapsed: {totalRunTime}");
        // this.logger.addRuntimeData(totalRunTime);
    }
    else
    {
        Console.WriteLine("Could not get target process");
    }
}

private static DateTime GetDateTimeOrDefault(PropertyData managementDateProperty, DateTime defaultValue)
{
    string dateString = managementDateProperty.Value as string;
    if (!string.IsNullOrEmpty(dateString))
    {
        return ManagementDateTimeConverter.ToDateTime(dateString);
    }
    else
    {
        return defaultValue;
    }
}

【讨论】:

  • CreationDate 属性是否保证在ProcessEnded 事件触发时设置?
  • 无论哪种方式,您的答案中的解决方案最适合我的用例。标记为正确答案。谢谢!
  • 我相信是的。 CreationDate 是Win32_Process class 中唯一标记为“Fixed”(意味着它不会更改)的属性。
【解决方案2】:

关于01/01/0001 00:00:01,你基本上是对的。

创建 DateTime 对象从 1/1/0001 12:00:00 AM 开始。您评论说 processStart 是在没有初始化的情况下声明的,这意味着它的默认开始时间是 1/1/0001 12:00:00 AM。我猜在 ProcessStart 被触发之前 ProcessEnd 被触发了。

下面的代码产生与您描述的类似的结果,并使用声明后未更改的 DateTime。

private static DateTime date;

public static void Main(string[] args) {
    const double largeVal = 63619141321;

    Console.WriteLine(date.ToString());

    double totalSeconds = (DateTime.Now - date).TotalSeconds;
    Console.WriteLine(totalSeconds - largeVal);

    Console.WriteLine("Press any key to continue . . . ");
    Console.ReadKey(true);
}

【讨论】:

  • 但是我将processStart初始化为DateTime.Now,而不是new DateTime()
  • @mjones.udri 你能评论定义 processStart 的那一行吗?
  • 它是一个类级变量; private DateTime processStart;,然后直到 ProcessStarted 事件处理程序触发时才被初始化; this.processStart = DateTime.Now;
  • @mjones.udri 当您在类级别声明 DateTime processStart; 而没有初始化程序时,编译器会在隐式 DateTime processStart = default(DateTime); 中添加,default(DateTime) 的值与 new DateTime() 的值相同,因为DateTime 是一个结构而不是一个类。所以你确实将processStart 初始化为与new DateTime() 相同的值。
  • @mjones.udri 是的。作为一种解决方法,您可以检查processStart == default(DateTime),如果是这样,则假设(DateTime.Now - this.processStart) 的结果是TimeSpan.Zero批次内的订单不受限制。当您有一个程序在一秒钟内打开和关闭时,打开事件和关闭事件都会在同一批次中结束。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多