문제

둘 다 System.Timers.Timer 그리고 System.Threading.Timer 요청 된 것과 상당한 간격으로 화재. 예를 들어:

new System.Timers.Timer(1000d / 20);

20이 아닌 초당 16 회 발사되는 타이머를 생성합니다.

너무 긴 이벤트 핸들러의 부작용이 없도록하기 위해이 작은 테스트 프로그램을 작성했습니다.

int[] frequencies = { 5, 10, 15, 20, 30, 50, 75, 100, 200, 500 };

// Test System.Timers.Timer
foreach (int frequency in frequencies)
{
    int count = 0;

    // Initialize timer
    System.Timers.Timer timer = new System.Timers.Timer(1000d / frequency);
    timer.Elapsed += delegate { Interlocked.Increment(ref count); };

    // Count for 10 seconds
    DateTime start = DateTime.Now;
    timer.Enabled = true;
    while (DateTime.Now < start + TimeSpan.FromSeconds(10))
        Thread.Sleep(10);
    timer.Enabled = false;

    // Calculate actual frequency
    Console.WriteLine(
        "Requested frequency: {0}\nActual frequency: {1}\n",
        frequency, count / 10d);
}

출력은 다음과 같습니다.

요청 : 5 Hz; 실제 : 4,8 Hz
요청 : 10 Hz; 실제 : 9,1 Hz
요청 : 15 Hz; 실제 : 12,7 Hz
요청 : 20 Hz; 실제 : 16Hz
요청 : 30 Hz; 실제 : 21,3Hz
요청 : 50 Hz; 실제 : 31,8 Hz
요청 : 75 Hz; 실제 : 63,9 Hz
요청 : 100Hz; 실제 : 63,8 Hz
요청 : 200 Hz; 실제 : 63,9 Hz
요청 : 500 Hz; 실제 : 63,9 Hz

실제 주파수는 요청 된 것보다 최대 36%까지 벗어납니다. (그리고 분명히 64Hz를 초과 할 수는 없습니다.) Microsoft 가이 타이머를 "더 큰 정확도"로 추천한다는 점을 감안할 때 System.Windows.Forms.Timer, 이것은 나를 퍼즐합니다.

BTW, 이들은 무작위 편차가 아닙니다. 그들은 매번 같은 값입니다. 다른 타이머 클래스에 대한 유사한 테스트 프로그램, System.Threading.Timer, 정확히 동일한 결과를 보여줍니다.

실제 프로그램에서는 초당 50 개의 샘플에서 측정 값을 수집해야합니다. 아직 실시간 시스템이 필요하지 않아야합니다. 50 대신 초당 32 개의 샘플을 얻는 것은 매우 실망 스럽습니다.

어떤 아이디어?

@chris : 당신이 옳습니다. 간격은 모두 1/64 초 정도의 무언가의 정수 배수 인 것 같습니다. BTW, 이벤트 핸들러에 Thread.sleep (...)를 추가하면 아무런 차이가 없습니다. 이것은 의미가 있습니다 System.Threading.Timer 스레드 풀을 사용하므로 각 이벤트가 무료 스레드에서 발사됩니다.

도움이 되었습니까?

해결책

글쎄, 나는 실제로 큰 편차를 가지고 100Hz까지 다른 숫자를 얻고 있지만 대부분의 경우 요청 된 숫자에 가깝습니다 (가장 최근 .NET SPS로 XP SP3 실행).

System.Timer.Timer는 System.threading.timer를 사용하여 구현되므로 동일한 결과가 표시되는 이유를 설명합니다. 타이머가 일종의 스케줄링 알고리즘 등을 사용하여 구현된다고 가정합니다 (내부 호출 일 것입니다. 아마도 로터 2.0을 보면 약간의 빛을 비출 수 있습니다).

다른 스레드 (또는 그 조합) 호출 수면 및 콜백을 사용하여 일종의 타이머를 구현하는 것이 좋습니다. 그래도 결과에 대해 확실하지 않습니다.

그렇지 않으면 당신은 볼 수 있습니다 멀티미디어 타이머 (Pinvoke).

다른 팁

winmm.dll을 사용하면 더 많은 CPU 시간을 사용할 수 있지만 더 나은 제어 기능이 있습니다.

다음은 winmm.dll 타이머를 사용하도록 수정 된 예입니다.

const String WINMM = "winmm.dll";
const String KERNEL32 = "kernel32.dll";

delegate void MMTimerProc (UInt32 timerid, UInt32 msg, IntPtr user, UInt32 dw1, UInt32 dw2);

[DllImport(WINMM)]
static extern uint timeSetEvent(
      UInt32            uDelay,      
      UInt32            uResolution, 
      [MarshalAs(UnmanagedType.FunctionPtr)] MMTimerProc lpTimeProc,  
      UInt32            dwUser,      
      Int32             fuEvent      
    );

[DllImport(WINMM)]
static extern uint timeKillEvent(uint uTimerID);

// Library used for more accurate timing
[DllImport(KERNEL32)]
static extern bool QueryPerformanceCounter(out long PerformanceCount);
[DllImport(KERNEL32)]
static extern bool QueryPerformanceFrequency(out long Frequency);

static long CPUFrequency;

static int count;

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

    int[] frequencies = { 5, 10, 15, 20, 30, 50, 75, 100, 200, 500 };

    foreach (int freq in frequencies)
    {
        count = 0;

        long start = GetTimestamp();

        // start timer
        uint timerId = timeSetEvent((uint)(1000 / freq), 0, new MMTimerProc(TimerFunction), 0, 1);

        // wait 10 seconds
        while (DeltaMilliseconds(start, GetTimestamp()) < 10000)
        {
            Thread.Sleep(1);
        }

        // end timer
        timeKillEvent(timerId);

        Console.WriteLine("Requested frequency: {0}\nActual frequency: {1}\n", freq, count / 10);
    }

    Console.ReadLine();
}

static void TimerFunction(UInt32 timerid, UInt32 msg, IntPtr user, UInt32 dw1, UInt32 dw2)
{
    Interlocked.Increment(ref count);
}

static public long DeltaMilliseconds(long earlyTimestamp, long lateTimestamp)
{
    return (((lateTimestamp - earlyTimestamp) * 1000) / CPUFrequency);
}

static public long GetTimestamp()
{
    long result;
    QueryPerformanceCounter(out result);
    return result;
}

그리고 여기에 내가 얻는 출력이 있습니다.

Requested frequency: 5
Actual frequency: 5

Requested frequency: 10
Actual frequency: 10

Requested frequency: 15
Actual frequency: 15

Requested frequency: 20
Actual frequency: 19

Requested frequency: 30
Actual frequency: 30

Requested frequency: 50
Actual frequency: 50

Requested frequency: 75
Actual frequency: 76

Requested frequency: 100
Actual frequency: 100

Requested frequency: 200
Actual frequency: 200

Requested frequency: 500
Actual frequency: 500

도움이 되었기를 바랍니다.

이 클래스는 실시간 사용을위한 것이 아니며 Windows와 같은 운영 체제의 역동적 인 스케줄링 특성에 따릅니다. 실시간 실행이 필요한 경우 임베디드 하드웨어를보고 싶을 것입니다. 100% 확실하지는 않지만 .NETCPU는 칩에서 .NET 런타임의 실시간 버전 일 수 있다고 생각합니다.

http://www.arm.com/markets/emerging_applications/armpp/8070.html

물론 - 해당 간격의 정확도가 얼마나 중요한지 평가해야합니다. 물론 이것은 순전히 학문적 질문이 아니라면 (어떤 경우 - 예, 흥미 롭습니다! : p).

실제 타이머 주파수는 63.9Hz 또는 정수 배수 인 것 같습니다.

이는 약 15msec의 타이머 해상도 (또는 정수 배수, 즉 30msec, 45msec 등)를 의미합니다.

이것은 '진드기'의 정수 배수를 기반으로 한 타이머가 예상됩니다 (예 : '진드기'값은 55msec / 18Hz였습니다).

나는 당신의 진드기 수가 15msec 대신 15.65 MEC인지 모르겠습니다. 실험으로 타이머 핸들러 내에서 여러 MSEC에서 잠을 자면 어떻게해야합니까? 진드기 사이에 15msec와 각 진드기의 타이머 핸들러에서 0.65msec가 표시 될 수 있습니까?

Windows (및 그 위에 실행되는 .NET)는 선제 적으로 멀티 태스킹 운영 체제입니다. 주어진 스레드는 다른 스레드로 언제든지 중지 될 수 있으며 선점 스레드가 제대로 작동하지 않으면 원할 때 제어 할 수 없습니다. 필요 그것.

간단히 말해서, 정확한 타이밍을 얻을 수없는 이유와 Windows와 .NET가 특정 유형의 소프트웨어에 부적합한 플랫폼 인 이유입니다. 원할 때 정확히 제어 할 수 없기 때문에 생명이 위험에 처한 경우 다른 플랫폼을 선택하십시오.

실시간 환경으로 점프해야한다면, 결정적인 샘플링이 필요할 때 (맞춤 직렬 장치에서) 과거에 RTX를 사용했으며 운이 좋았습니다.

http://www.pharlap.com/rtx.htm

다음은 멀티미디어 타이머를 사용하여 타이머를 잘 구현 한 것입니다.http://www.softwareintercations.com/blog/2009/12/7/using-the-multimedia-timer-from-c.html

타이머 주파수가 꺼지는 데는 많은 이유가 있습니다. 하드웨어의 예, 같은 스레드는 다른 처리로 바쁘다.

보다 정확하게 시간을 원한다면 System.Diagnostics 네임 스페이스에서 StopWatch 클래스를 사용하십시오.

문제의 일부는 타이머에 두 가지가 있다는 것입니다 지연 계정에. 이는 OS에서 타이머가 구현되는 방식에 따라 다를 수 있습니다.

  1. 기다리는 시간
  2. #1이 발생하고 프로세스가 스케줄링 큐에서 회전하는 시간 사이의 시간.

타이머는 #1을 잘 제어하지만 #2를 거의 제어 할 수 없습니다. OS에 다시 실행되기를 원한다는 신호를 보낼 수 있지만 OS는 기분이 좋을 때 자유롭게 깨울 수 있습니다.

의견을 바탕으로 타이머를 전혀 사용해서는 안됩니다. Quantum을 잃지 않도록 간격과 스핀 록을 확인하기 위해 스톱워치가있는 루프를 사용해야합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top