Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# System.Timers.Timer每秒最多只能提供64帧_C#_.net_Winforms_Timer - Fatal编程技术网

C# System.Timers.Timer每秒最多只能提供64帧

C# System.Timers.Timer每秒最多只能提供64帧,c#,.net,winforms,timer,C#,.net,Winforms,Timer,我有一个应用程序,它使用System.Timers.Timer对象引发由主窗体(,C#)处理的事件。我的问题是,无论我将.Interval设置得多么短(甚至是1ms),我每秒最多只能得到64次 我知道表单计时器有55毫秒的精度限制,但这是系统。计时器变量,而不是表单 应用程序占用1%的CPU,因此它肯定不受CPU限制。所以它所做的就是: 将计时器设置为1&nsp;ms 当事件触发时,增加一个_Count变量 将其设置为1&nsp;再重复一遍 _即使没有其他工作要做,计数也最多每秒递增64次

我有一个应用程序,它使用System.Timers.Timer对象引发由主窗体(,C#)处理的事件。我的问题是,无论我将.Interval设置得多么短(甚至是1ms),我每秒最多只能得到64次

我知道表单计时器有55毫秒的精度限制,但这是系统。计时器变量,而不是表单

应用程序占用1%的CPU,因此它肯定不受CPU限制。所以它所做的就是:

  • 将计时器设置为1&nsp;ms
  • 当事件触发时,增加一个_Count变量
  • 将其设置为1&nsp;再重复一遍
_即使没有其他工作要做,计数也最多每秒递增64次

这是一个“回放”应用程序,它必须复制传入的数据包,它们之间的延迟只有1-2ms,因此我需要一种每秒可以可靠触发1000次左右的数据包(尽管如果我是CPU受限的,我会接受100次,但我不是)

有什么想法吗?

试试——它们为硬件平台提供了尽可能高的精确度。这些计时器以比其他计时器服务更高的分辨率安排事件

您需要以下Win API函数来设置计时器分辨率、启动和停止计时器:

[DllImport("winmm.dll")]
private static extern int timeGetDevCaps(ref TimerCaps caps, int sizeOfTimerCaps);

[DllImport("winmm.dll")]
private static extern int timeSetEvent(int delay, int resolution, TimeProc proc, int user, int mode);

[DllImport("winmm.dll")]
private static extern int timeKillEvent(int id);
您还需要回调委托:

delegate void TimeProc(int id, int msg, int user, int param1, int param2);
和定时器功能结构

[StructLayout(LayoutKind.Sequential)]
public struct TimerCaps
{
    public int periodMin;
    public int periodMax;
}
用法:

TimerCaps caps = new TimerCaps();
// provides min and max period 
timeGetDevCaps(ref caps, Marshal.SizeOf(caps));
int period = 1;
int resolution = 1;
int mode = 0; // 0 for periodic, 1 for single event
timeSetEvent(period, resolution, new TimeProc(TimerCallback), 0, mode);
和回调:

void TimerCallback(int id, int msg, int user, int param1, int param2)
{
    // occurs every 1 ms
}

你可以坚持你的设计。您只需将系统中断频率设置为以其最大频率运行即可。为了实现这一点,您只需在代码中的任意位置执行以下代码:

#define TARGET_RESOLUTION 1         // 1-millisecond target resolution

TIMECAPS tc;
UINT     wTimerRes;

if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) 
{
    // Error; application can't continue.
}

wTimerRes = min(max(tc.wPeriodMin, TARGET_RESOLUTION), tc.wPeriodMax);
timeBeginPeriod(wTimerRes); 
这将迫使系统中断周期以最大频率运行。这是一个系统范围的行为,因此它甚至可以在单独的过程中完成。别忘了使用

MMRESULT timeEndPeriod(wTimerRes );
完成后,释放资源并将中断周期重置为默认值。有关详细信息,请参阅

您必须将对
timeBeginPeriod
的每次调用与对
timeEndPeriod
的调用相匹配,并在两次调用中指定相同的最小分辨率。只要每个调用与对
timeEndPeriod
的调用相匹配,应用程序就可以进行多个
timeBeginPeriod
调用

因此,所有计时器(包括您当前的设计)都将以更高的频率运行,因为计时器的粒度将得到改善。在大多数硬件上可以获得1ms的粒度

以下是通过两种不同硬件设置(a+B)的
wTimerRes
的各种设置获得的中断周期列表:

很容易看出,1ms是一个理论值。实际分辨率以100ns为单位。9766表示0.9766ms,即每秒1024次中断。(事实上,它应该是0.9765625,即97656.25 100ns单位,但该精度显然不适合整数,因此由系统四舍五入。)

同样显而易见的是,平台A并不真正支持
timeGetDevCaps
返回的所有时段范围(值范围介于
wPeriodMin
wPeriodMin
之间)


摘要:多媒体定时器接口可用于修改系统范围内的中断频率。因此,所有计时器都将更改其粒度。系统时间更新也将相应地改变,它将以更小的步骤更频繁地增加但是:实际行为取决于底层硬件。自从Windows7和Windows8推出以来,这种硬件依赖性变得更小了,因为新的计时方案已经引入

基于其他解决方案和注释,我编写了这段VB.NET代码。可以使用表单粘贴到项目中。我理解@HansPassant的评论说,只要调用
timeBeginPeriod
,“常规计时器也会变得准确”。在我的代码中似乎不是这样

我的代码在使用
timeBeginPeriod
将计时器分辨率设置为最小值后,创建了一个多媒体计时器、一个
System.Threading.timer
、一个
System.Timers.timer
、一个
Windows.Forms.timer
。多媒体计时器按要求以1 kHz的频率运行,但其他计时器仍停留在64 Hz的频率。因此,要么我做错了什么,要么就无法更改内置.NET计时器的分辨率

编辑;将代码更改为使用StopWatch类计时

Imports System.Runtime.InteropServices
Public Class Form1

    'From http://www.pinvoke.net/default.aspx/winmm/MMRESULT.html
    Private Enum MMRESULT
        MMSYSERR_NOERROR = 0
        MMSYSERR_ERROR = 1
        MMSYSERR_BADDEVICEID = 2
        MMSYSERR_NOTENABLED = 3
        MMSYSERR_ALLOCATED = 4
        MMSYSERR_INVALHANDLE = 5
        MMSYSERR_NODRIVER = 6
        MMSYSERR_NOMEM = 7
        MMSYSERR_NOTSUPPORTED = 8
        MMSYSERR_BADERRNUM = 9
        MMSYSERR_INVALFLAG = 10
        MMSYSERR_INVALPARAM = 11
        MMSYSERR_HANDLEBUSY = 12
        MMSYSERR_INVALIDALIAS = 13
        MMSYSERR_BADDB = 14
        MMSYSERR_KEYNOTFOUND = 15
        MMSYSERR_READERROR = 16
        MMSYSERR_WRITEERROR = 17
        MMSYSERR_DELETEERROR = 18
        MMSYSERR_VALNOTFOUND = 19
        MMSYSERR_NODRIVERCB = 20
        WAVERR_BADFORMAT = 32
        WAVERR_STILLPLAYING = 33
        WAVERR_UNPREPARED = 34
    End Enum

    'http://msdn.microsoft.com/en-us/library/windows/desktop/dd757625(v=vs.85).aspx
    <StructLayout(LayoutKind.Sequential)>
    Public Structure TIMECAPS
        Public periodMin As UInteger
        Public periodMax As UInteger
    End Structure

    'http://msdn.microsoft.com/en-us/library/windows/desktop/dd757627(v=vs.85).aspx
    <DllImport("winmm.dll")>
    Private Shared Function timeGetDevCaps(ByRef ptc As TIMECAPS, ByVal cbtc As UInteger) As MMRESULT
    End Function

    'http://msdn.microsoft.com/en-us/library/windows/desktop/dd757624(v=vs.85).aspx
    <DllImport("winmm.dll")>
    Private Shared Function timeBeginPeriod(ByVal uPeriod As UInteger) As MMRESULT
    End Function

    'http://msdn.microsoft.com/en-us/library/windows/desktop/dd757626(v=vs.85).aspx
    <DllImport("winmm.dll")>
    Private Shared Function timeEndPeriod(ByVal uPeriod As UInteger) As MMRESULT
    End Function

    'http://msdn.microsoft.com/en-us/library/windows/desktop/ff728861(v=vs.85).aspx
    Private Delegate Sub TIMECALLBACK(ByVal uTimerID As UInteger, _
                                  ByVal uMsg As UInteger, _
                                  ByVal dwUser As IntPtr, _
                                  ByVal dw1 As IntPtr, _
                                  ByVal dw2 As IntPtr)

    'Straight from C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include\MMSystem.h
    'fuEvent below is a combination of these flags.
    Private Const TIME_ONESHOT As UInteger = 0
    Private Const TIME_PERIODIC As UInteger = 1
    Private Const TIME_CALLBACK_FUNCTION As UInteger = 0
    Private Const TIME_CALLBACK_EVENT_SET As UInteger = &H10
    Private Const TIME_CALLBACK_EVENT_PULSE As UInteger = &H20
    Private Const TIME_KILL_SYNCHRONOUS As UInteger = &H100

    'http://msdn.microsoft.com/en-us/library/windows/desktop/dd757634(v=vs.85).aspx
    'Documentation is self-contradicting. The return value is Uinteger, I'm guessing.
    '"Returns an identifier for the timer event if successful or an error otherwise. 
    'This function returns NULL if it fails and the timer event was not created."
    <DllImport("winmm.dll")>
    Private Shared Function timeSetEvent(ByVal uDelay As UInteger, _
                                         ByVal uResolution As UInteger, _
                                         ByVal TimeProc As TIMECALLBACK, _
                                         ByVal dwUser As IntPtr, _
                                         ByVal fuEvent As UInteger) As UInteger
    End Function

    'http://msdn.microsoft.com/en-us/library/windows/desktop/dd757630(v=vs.85).aspx
    <DllImport("winmm.dll")>
    Private Shared Function timeKillEvent(ByVal uTimerID As UInteger) As MMRESULT
    End Function

    Private lblRate As New Windows.Forms.Label
    Private WithEvents tmrUI As New Windows.Forms.Timer
    Private WithEvents tmrWorkThreading As New System.Threading.Timer(AddressOf TimerTick)
    Private WithEvents tmrWorkTimers As New System.Timers.Timer
    Private WithEvents tmrWorkForm As New Windows.Forms.Timer

    Public Sub New()
        lblRate.AutoSize = True
        Me.Controls.Add(lblRate)

        InitializeComponent()
    End Sub

    Private Capability As New TIMECAPS

    Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        timeKillEvent(dwUser)
        timeEndPeriod(Capability.periodMin)
    End Sub

    Private dwUser As UInteger = 0
    Private Clock As New System.Diagnostics.Stopwatch
    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) _
        Handles MyBase.Load

        Dim Result As MMRESULT

        'Get the min and max period
        Result = timeGetDevCaps(Capability, Marshal.SizeOf(Capability))
        If Result <> MMRESULT.MMSYSERR_NOERROR Then
            MsgBox("timeGetDevCaps returned " + Result.ToString)
            Exit Sub
        End If

        'Set to the minimum period.
        Result = timeBeginPeriod(Capability.periodMin)
        If Result <> MMRESULT.MMSYSERR_NOERROR Then
            MsgBox("timeBeginPeriod returned " + Result.ToString)
            Exit Sub
        End If

        Clock.Start()

        Dim uTimerID As UInteger
        uTimerID = timeSetEvent(Capability.periodMin, Capability.periodMin, _
                     New TIMECALLBACK(AddressOf MMCallBack), dwUser, _
                     TIME_PERIODIC Or TIME_CALLBACK_FUNCTION Or TIME_KILL_SYNCHRONOUS)
        If uTimerID = 0 Then
            MsgBox("timeSetEvent not successful.")
            Exit Sub
        End If

        tmrWorkThreading.Change(0, 1)

        tmrWorkTimers.Interval = 1
        tmrWorkTimers.Enabled = True

        tmrWorkForm.Interval = 1
        tmrWorkForm.Enabled = True

        tmrUI.Interval = 100
        tmrUI.Enabled = True
    End Sub

    Private CounterThreading As Integer = 0
    Private CounterTimers As Integer = 0
    Private CounterForms As Integer = 0
    Private CounterMM As Integer = 0

    Private ReadOnly TimersLock As New Object
    Private Sub tmrWorkTimers_Elapsed(sender As Object, e As System.Timers.ElapsedEventArgs) _
        Handles tmrWorkTimers.Elapsed
        SyncLock TimersLock
            CounterTimers += 1
        End SyncLock
    End Sub

    Private ReadOnly ThreadingLock As New Object
    Private Sub TimerTick()
        SyncLock ThreadingLock
            CounterThreading += 1
        End SyncLock
    End Sub

    Private ReadOnly MMLock As New Object
    Private Sub MMCallBack(ByVal uTimerID As UInteger, _
                                  ByVal uMsg As UInteger, _
                                  ByVal dwUser As IntPtr, _
                                  ByVal dw1 As IntPtr, _
                                  ByVal dw2 As IntPtr)
        SyncLock MMLock
            CounterMM += 1
        End SyncLock
    End Sub

    Private ReadOnly FormLock As New Object
    Private Sub tmrWorkForm_Tick(sender As Object, e As System.EventArgs) Handles tmrWorkForm.Tick
        SyncLock FormLock
            CounterForms += 1
        End SyncLock
    End Sub

    Private Sub tmrUI_Tick(sender As Object, e As System.EventArgs) _
    Handles tmrUI.Tick
        Dim Secs As Integer = Clock.Elapsed.TotalSeconds
        If Secs > 0 Then
            Dim TheText As String = ""
            TheText += "System.Threading.Timer " + (CounterThreading / Secs).ToString("#,##0.0") + "Hz" + vbCrLf
            TheText += "System.Timers.Timer " + (CounterTimers / Secs).ToString("#,##0.0") + "Hz" + vbCrLf
            TheText += "Windows.Forms.Timer " + (CounterForms / Secs).ToString("#,##0.0") + "Hz" + vbCrLf
            TheText += "Multimedia Timer " + (CounterMM / Secs).ToString("#,##0.0") + "Hz"
            lblRate.Text = TheText
        End If
    End Sub

End Class
导入System.Runtime.InteropServices
公开课表格1
“从http://www.pinvoke.net/default.aspx/winmm/MMRESULT.html
私有枚举结果
MMSYSERR\u NOERROR=0
MMSYSERR_错误=1
MMSYSERR_BADDEVICEID=2
MMSYSERR_NOTENABLED=3
MMSYSERR_已分配=4
MMSYSERR_=5
MMSYSERR_NODRIVER=6
MMSYSERR_NOMEM=7
MMSYSERR_不受支持=8
MMSYSERR_badernum=9
MMSYSERR_invaliflag=10
MMSYSERR_invaliparam=11
MMSYSERR_车把Y=12
MMSSYSERR_残疾人=13
MMSYSERR_BADDB=14
MMSYSERR_KEYNOTFOUND=15
MMSYSERR_READERROR=16
MMSYSERR_WRITEERROR=17
MMSYSERR_DELETEERROR=18
MMSYSERR_VALNOTFOUND=19
MMSYSERR_NODRIVERCB=20
WAVERR_BADFORMAT=32
WAVERR_仍在播放=33
WAVERR_未准备好=34
结束枚举
'http://msdn.microsoft.com/en-us/library/windows/desktop/dd757625(v=vs.85).aspx
公共结构时限
公共周期作为UInteger
公共周期最大值为UInteger
端部结构
'http://msdn.microsoft.com/en-us/library/windows/desktop/dd757627(v=vs.85).aspx
私有共享函数timeGetDevCaps(ByRef ptc作为TIMECAPS,ByVal cbtc作为UInteger)作为MMRESULT
端函数
'http://msdn.microsoft.com/en-us/library/windows/desktop/dd757624(v=vs.85).aspx
私有共享函数timeBeginPeriod(ByVal uPeriod作为UInteger)作为MMRESULT
结束乐趣