【问题标题】:TimeBeginPeriod / TimeEndPeriod SequenceTimeBeginPeriod / TimeEndPeriod 序列
【发布时间】:2021-11-06 16:28:36
【问题描述】:

我有两个 Windows .NET 库 A 和 B。库 A 有 TimeBeginPeriod(1) 和 TimeEndPeriod(1),而库 B 有 TimeBeginPeriod(5) 和 TimeEndPeriod(5)。我最近注意到(在他们的behavor change 之后)当图书馆 B 将执行 TimeEndPeriod(5) 图书馆 A 失去 TimeBeginPeriod(1)。

您能否在同一个应用程序中有不同的时间段而不影响另一个时间段?我的意思是我不介意 (5) 是否会受到 (1) 的影响,但至关重要的是 end(5) 不会影响 begin(1)!

LibA TimeBeginPeriod(1)
.... LibB TimeBeginPeriod(5)
.... LibB TimeEndPeriod(5)    <- Does this cancel TimeBeginPeriod(1)?
LibA TimeEndPeriod(1)

[更新]

测试代码

using System;
using System.Runtime.InteropServices;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        static long freq;

        static void Main(string[] args)
        {
            QueryPerformanceFrequency(out freq);

            Console.WriteLine("Begin");
            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());

            TimeBeginPeriod(1);
            Console.WriteLine("TimeBeginPeriod(1)");
            Thread.Sleep(5000);

            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());

            TimeBeginPeriod(15);
            Console.WriteLine("TimeBeginPeriod(15)");
            Thread.Sleep(5000);

            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());

            Console.WriteLine("TimeEndPeriod(15)");
            TimeEndPeriod(15);
            Thread.Sleep(5000);

            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());

            TimeEndPeriod(1);
            Console.WriteLine("TimeEndPeriod(1)");


            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());
            Console.WriteLine("End");
        }

        private static string CalcSleep1()
        {
            QueryPerformanceCounter(out long ticks1);
            Thread.Sleep(1);
            QueryPerformanceCounter(out long ticks2);
            return (new TimeSpan((long)(1000 * 10000 * (ticks2 - ticks1) / (double)freq))).ToString();
        }


        [DllImport("Kernel32.dll")]
        private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);

        [DllImport("Kernel32.dll")]
        private static extern bool QueryPerformanceFrequency(out long lpFrequency);

        [DllImport("winmm.dll", EntryPoint = "timeBeginPeriod", SetLastError = true)]
        public static extern uint TimeBeginPeriod(uint uMilliseconds);

        [DllImport("winmm.dll", EntryPoint = "timeEndPeriod", SetLastError = true)]
        public static extern uint TimeEndPeriod(uint uMilliseconds);

        [DllImport("winmm.dll", ExactSpelling = true)]
        private static extern int timeGetDevCaps(ref TIMECAPS ptc, int cbtc);

        [StructLayout(LayoutKind.Sequential)]
        private struct TIMECAPS
        {
            internal int wPeriodMin;

            internal int wPeriodMax;
        }
    }
}

输出

Begin
00:00:00.0130730
00:00:00.0154164
TimeBeginPeriod(1)
00:00:00.0019935
00:00:00.0019148
00:00:00.0019145
00:00:00.0019203
00:00:00.0023354
TimeBeginPeriod(15)
00:00:00.0019916
00:00:00.0018920
00:00:00.0019412
00:00:00.0019411
00:00:00.0019336
TimeEndPeriod(15)
00:00:00.0019918
00:00:00.0019300
00:00:00.0019671
00:00:00.0019524
00:00:00.0019399
TimeEndPeriod(1)
00:00:00.0152576
00:00:00.0148129
End

似乎介于 TimeBeginPeriod(15) / TimeEndPeriod(15) 之间不会影响 TimeBeginPeriod(1)。除非这只发生在不同的程序集上(尚未测试)

【问题讨论】:

  • 那么,您具体运行的是哪个 Windows 版本? docs.microsoft.com/en-us/windows/win32/api/timeapi/…
  • Windows 10 专业版 [版本 10.0.19043.1165]
  • 好吧,让我们暂时忘记“效果”。问题是,如果你有两个时间段,一个又一个,它们是如何相互影响的?
  • 进程中没有“时间段”的概念,每个进程只有一个值。将timeBeginPeriod(value) 实现视为processValue = Min(processValue, value),将timeEndPeriod(value) 实现视为if (processValue == value) processValue = defaultValue。否则,您的测试中还有另一个问题,您使用的是 15,但使用 15 通常与 1 具有相同的效果,因为标准时钟分辨率为 15.625ms。试试 16。
  • 我的应用程序中存在一个错误,并试图查找位置和原因。 LibB 可以在其他应用程序中使用,它不需要 timeBeginPeriod(1),但它确实需要 (5)。我不明白你的意思是 timeBeginPeriod(15) 与 timeBeginPeriod(1) 具有相同的效果?这怎么可能?

标签: .net windows winapi


【解决方案1】:

通过用一个较小的示例编写我的应用程序逻辑,我能够重现并识别一个严重的 Windows 错误。 当您动态加载程序集时会发生这种情况。您可以下载解决方案和/或编译here

更新:它发生在静态/动态上。当全局定时器改变时,它有不同的行为。我的应用程序正在做的事情是 Windows 不喜欢的:

.... LibB TimeBeginPeriod(5)
LibA TimeBeginPeriod(1)
.... LibB TimeEndPeriod(5)    <- This affects TimeBeginPeriod(1) goes back to 15ms - and it happens when the global timer is already to 1ms? - 
LibA TimeEndPeriod(1)

我敢打赌,这个问题会影响许多应用程序,因为他们并不真正知道底层库对时间段做了什么。

(将 Class12 dll 复制到控制台应用程序输出)

Class11

using System;
using System.Runtime.InteropServices;
using System.Threading;

namespace ClassLibrary1
{
    public abstract class ClassAbstract
    {
        public abstract void StartSample(uint ms);
        public abstract void EndSample(uint ms);
        public abstract void PrintSample();
    }

    public class Class11
    {
        static long freq;

        public Class11()
        {
            QueryPerformanceFrequency(out freq);
        }

        private static string CalcSleep1()
        {
            QueryPerformanceCounter(out long ticks1);
            Thread.Sleep(1);
            QueryPerformanceCounter(out long ticks2);
            return (new TimeSpan((long)(1000 * 10000 * (ticks2 - ticks1) / (double)freq))).ToString();
        }

        public void StartSample(uint ms)
        {
            TimeBeginPeriod(ms);
            Console.WriteLine($"TimeBeginPeriod({ms})");

            PrintSample();
        }

        public void EndSample(uint ms)
        {
            Console.WriteLine($"TimeEndPeriod({ms})");
            TimeEndPeriod(ms);
        }

        public void PrintSample()
        {
            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());
        }


        [DllImport("Kernel32.dll")]
        private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);

        [DllImport("Kernel32.dll")]
        private static extern bool QueryPerformanceFrequency(out long lpFrequency);

        [DllImport("winmm.dll", EntryPoint = "timeBeginPeriod", SetLastError = true)]
        public static extern uint TimeBeginPeriod(uint uMilliseconds);

        [DllImport("winmm.dll", EntryPoint = "timeEndPeriod", SetLastError = true)]
        public static extern uint TimeEndPeriod(uint uMilliseconds);
    }
}

第 12 课

using System;
using System.Runtime.InteropServices;
using System.Threading;

using ClassLibrary1;

namespace ClassLibrary2
{
    public class Class12 : ClassAbstract
    {
        static long freq;

        public Class12()
        {
            QueryPerformanceFrequency(out freq);
        }

        private static string CalcSleep1()
        {
            QueryPerformanceCounter(out long ticks1);
            Thread.Sleep(1);
            QueryPerformanceCounter(out long ticks2);
            return (new TimeSpan((long)(1000 * 10000 * (ticks2 - ticks1) / (double)freq))).ToString();
        }

        public override void StartSample(uint ms)
        {
            TimeBeginPeriod(ms);
            Console.WriteLine($"TimeBeginPeriod({ms})");

            PrintSample();
        }

        public override void EndSample(uint ms)
        {
            Console.WriteLine($"TimeEndPeriod({ms})");
            TimeEndPeriod(ms);
        }

        public override void PrintSample()
        {
            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());
            Console.WriteLine(CalcSleep1());
        }


        [DllImport("Kernel32.dll")]
        private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);

        [DllImport("Kernel32.dll")]
        private static extern bool QueryPerformanceFrequency(out long lpFrequency);

        [DllImport("winmm.dll", EntryPoint = "timeBeginPeriod", SetLastError = true)]
        public static extern uint TimeBeginPeriod(uint uMilliseconds);

        [DllImport("winmm.dll", EntryPoint = "timeEndPeriod", SetLastError = true)]
        public static extern uint TimeEndPeriod(uint uMilliseconds);
    }
}

控制台应用程序

using System;
using System.IO;
using System.Reflection;

using ClassLibrary1;

namespace ConsoleApp1
{
    class Program
    {
        static Class11 class11;
        static ClassAbstract class12;

        static void Main(string[] args)
        {
            class11 = new Class11();

            var assembly =  Assembly.LoadFrom(Path.GetFullPath(@"ClassLibrary2.dll"));
            ClassAbstract class12 = (ClassAbstract) Activator.CreateInstance(assembly.GetTypes()[0]);

            // Behavior changes while System global time period changes
            class12.StartSample(5); // Bug here we get 1ms instead of 5ms
            class11.StartSample(1);
            class12.EndSample(5);
            class11.PrintSample(); // Bug Here we get 15 ms period instead of 1ms
            class11.EndSample(1);
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-31
    • 2020-11-02
    • 1970-01-01
    • 1970-01-01
    • 2021-07-08
    • 1970-01-01
    • 2021-06-12
    相关资源
    最近更新 更多