C# 如何从秒表生成滴答事件
我正在执行数据采集读取温度等,我正在尝试想出一种替代Windows窗体计时器的方法,因为它在很长一段时间内会显著漂移。我想将秒表封装在一个类中,该类每秒将触发事件10次,这将触发样本温度值的收集。我还需要能够用当前数据更新图表,尽管收集的时间准确性比保持表单更新更重要。数据采集一次可以运行几个小时,据我所知,秒表比windows窗体计时器工作得更好,因为准确度很重要C# 如何从秒表生成滴答事件,c#,C#,我正在执行数据采集读取温度等,我正在尝试想出一种替代Windows窗体计时器的方法,因为它在很长一段时间内会显著漂移。我想将秒表封装在一个类中,该类每秒将触发事件10次,这将触发样本温度值的收集。我还需要能够用当前数据更新图表,尽管收集的时间准确性比保持表单更新更重要。数据采集一次可以运行几个小时,据我所知,秒表比windows窗体计时器工作得更好,因为准确度很重要 所以我的问题是:包装秒表以给定间隔10 Hz触发事件的好方法是什么?创建一个带有循环的线程,该循环不断检查当前秒表时间,并在秒表时
所以我的问题是:包装秒表以给定间隔10 Hz触发事件的好方法是什么?创建一个带有循环的线程,该循环不断检查当前秒表时间,并在秒表时间处于所需值时触发事件?或者是否有其他更适合此应用程序的计时器?秒表类不推送任何东西。它只是坐在那里,当你问它时,它会告诉你经过了多少时间。要问它已经过了多少时间,您需要一个事件。我建议使用Timers.Timer,看看是否更好,如果不是,然后看看使用高分辨率计时器:我最后测试了几个不同的选项,并认为收集的信息可能对那里的人有用。尽管如此,我还是在漂移值中得到了一些古怪的值,计时器值离日期时间开始和结束时间有多远,我猜这是日期时间准确性的问题,或者我在代码中搞错了什么。尽管如此,多媒体定时器显然是我的应用程序的赢家 1000毫秒间隔 100毫秒间隔 50毫秒间隔 20毫秒间隔 10毫秒的时间间隔三个定时器在打架,所以我就用多媒体定时器 表格: 摘要计时器:
using System;
using System.Windows.Forms;
namespace TimerTest.Timing.Timers
{
/// <summary>
/// An abstract class for defining the structure of a timer
/// </summary>
public abstract class AbstractTimer
{
#region Public Events
/// <summary>
/// Event Raised when the Timer has elapsed
/// </summary>
public abstract event EventHandler<TickEventArgs> Tick;
#endregion
#region Properties
/// <summary>
/// Determines whether or not the timer is enabled
/// </summary>
public abstract bool Enabled { get; set; }
/// <summary>
/// The form which the timer will invoke the Tick event on
/// </summary>
public abstract Form Form { get; }
/// <summary>
/// The length of the interval in milliseconds
/// </summary>
public abstract double IntervalLength { get; set; }
/// <summary>
/// The number of intervals that have elapsed
/// </summary>
public abstract int IntervalsElapsed { get; }
/// <summary>
/// The name of the timer
/// </summary>
public abstract string Name { get; }
/// <summary>
/// The time at which the timer started
/// </summary>
public abstract DateTime StartTime { get; }
#endregion
#region Public Methods
/// <summary>
/// Star the timer
/// </summary>
public abstract void Start();
/// <summary>
/// Attempt to stop the timer
/// </summary>
/// <returns>True if the timer was successfully stopped, false if not</returns>
public abstract bool Stop();
#endregion
}
}
勾选事件参数:
using System;
namespace TimerTest.Timing.Timers
{
/// <summary>
/// Event Arguments for Tick Events
/// </summary>
public class TickEventArgs : EventArgs
{
#region Private Fields
private readonly DateTime _currentTime;
private readonly DateTime _startTime;
private readonly double _timerIntervalLength;
private readonly int _timerIntervalsElapsed;
#endregion
#region Properties
/// <summary>
/// The current time when the TickEventArgs were created
/// </summary>
public DateTime CurrentTime
{
get { return _currentTime; }
}
/// <summary>
/// The average number of milliseconds that the the timer has drifted away from the actual time each hour.
/// A negative value indicates that the timer is running slower than the set interval,
/// and a positive value indicates that the timer is running faster than the set interval
/// </summary>
public double DriftMsPerHour
{
get { return (DriftSecondsTotal*1000*60*60)/ElapsedTime.TotalSeconds; }
}
/// <summary>
/// The average number of milliseconds that the the timer has drifted away from the actual time on each interval.
/// A negative value indicates that the timer is running slower than the set interval,
/// and a positive value indicates that the timer is running faster than the set interval
/// </summary>
public double DriftMsPerInterval
{
get { return (DriftSecondsTotal*1000)/(ElapsedTime.TotalSeconds*(1000/TimerIntervalLength)); }
}
/// <summary>
/// The average number of milliseconds that the the timer has drifted away from the actual time each minute.
/// A negative value indicates that the timer is running slower than the set interval,
/// and a positive value indicates that the timer is running faster than the set interval
/// </summary>
public double DriftMsPerMinute
{
get { return (DriftSecondsTotal*1000*60)/(ElapsedTime.TotalSeconds); }
}
/// <summary>
/// The average number of milliseconds that the the timer has drifted away from the actual time each second.
/// A negative value indicates that the timer is running slower than the set interval,
/// and a positive value indicates that the timer is running faster than the set interval
/// </summary>
public double DriftMsPerSecond
{
get { return (DriftSecondsTotal*1000)/ElapsedTime.TotalSeconds; }
}
/// <summary>
/// The total number of seconds that the timer has drifted away from the actual time.
/// A negative value indicates that the timer is running slower than the set interval,
/// and a positive value indicates that the timer is running faster than the set interval
/// </summary>
public double DriftSecondsTotal
{
get { return TimerElapsedSeconds - ElapsedTime.TotalSeconds; }
}
/// <summary>
/// The actual number of seconds elapsed
/// </summary>
public TimeSpan ElapsedTime
{
get { return CurrentTime - StartTime; }
}
/// <summary>
/// The time that the timer started
/// </summary>
public DateTime StartTime
{
get { return _startTime; }
}
/// <summary>
/// The number of seconds elapsed by the timer
/// </summary>
public double TimerElapsedSeconds
{
get { return TimerIntervalsElapsed*TimerIntervalLength/1000; }
}
/// <summary>
/// The interval length of the timer
/// </summary>
public double TimerIntervalLength
{
get { return _timerIntervalLength; }
}
/// <summary>
/// The number of intervals elapsed by the timer
/// </summary>
public int TimerIntervalsElapsed
{
get { return _timerIntervalsElapsed; }
}
#endregion
#region Constructors
/// <summary>
/// Initializes an instance of TickEventArgs
/// </summary>
/// <param name="startTime"> The time that the timer started </param>
/// <param name="timerIntervalsElapsed"> The number of intervals elapsed by the timer </param>
/// <param name="timerIntervalLength"> The interval length of the timer </param>
/// <param name="currentTime"> The current time when the TickEventArgs were created </param>
public TickEventArgs(DateTime startTime, int timerIntervalsElapsed,
double timerIntervalLength, DateTime currentTime)
{
_startTime = startTime;
_timerIntervalsElapsed = timerIntervalsElapsed;
_timerIntervalLength = timerIntervalLength;
_currentTime = currentTime;
}
#endregion
}
}
多媒体定时器:
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace TimerTest.Timing.Timers
{
/// <summary>
/// A wrapper for a winmm.dll timer
/// </summary>
public class MultimediaTimer : AbstractTimer
{
#region Public Events
/// <summary>
/// Event Raised when the Timer has elapsed
/// </summary>
public override event EventHandler<TickEventArgs> Tick;
#endregion
#region Private Fields
private bool _enabled;
private readonly Form _form;
private double _intervalLength;
private int _intervalsElapsed;
private DateTime _startTime;
private UInt32 _timerId;
#endregion
#region Properties
public override bool Enabled
{
get { return _enabled; }
set
{
if (value)
{
Start();
}
else
{
Stop();
}
}
}
public override Form Form
{
get { return _form; }
}
public override double IntervalLength
{
get { return _intervalLength; }
set { _intervalLength = value; }
}
public override int IntervalsElapsed
{
get { return _intervalsElapsed; }
}
public override string Name
{
get { return "MultiMedia Timer"; }
}
public override DateTime StartTime
{
get { return _startTime; }
}
#endregion
#region Constructors
/// <summary>
/// Initializes an instance of MultimediaTimer
/// </summary>
/// <param name="form"> The form which the timer will invoke the Tick event on </param>
/// <param name="intervalLength"> The length of the interval in milliseconds </param>
public MultimediaTimer(Form form, double intervalLength)
{
_form = form;
_intervalLength = intervalLength;
}
#endregion
#region Public Methods
public override void Start()
{
if (Enabled)
return;
uint interval = Convert.ToUInt32(Math.Round(_intervalLength, 0));
_timerId = Winmm.Start(interval, TimerCallback);
_startTime = DateTime.Now;
_enabled = true;
}
public override bool Stop()
{
if (Enabled)
{
bool stopped = Winmm.Stop(_timerId);
_enabled = !stopped;
return stopped;
}
return false;
}
#endregion
#region Private Methods
private void TimerCallback(int id, int msg, IntPtr user, int dw1, int dw2)
{
_intervalsElapsed++;
if (Enabled)
{
TriggerTick();
}
}
private void TriggerTick()
{
DateTime currentTime = DateTime.Now;
_form.BeginInvoke((MethodInvoker) delegate
{
TickEventArgs tickEventArgs = new TickEventArgs(StartTime,
IntervalsElapsed,
IntervalLength,
currentTime);
if (Tick != null)
{
Tick(this, tickEventArgs);
}
});
}
#endregion
#region Nested Classes
/// <summary>
/// A static wrapper for Winmm.dll
/// </summary>
public static class Winmm
{
#region Static Private Fields
/// <summary>
/// The Event Type
/// </summary>
private const int _EVENT_TYPE = _TIME_PERIODIC; // + 0x100; // TIME_KILL_SYNCHRONOUS causes a hang ?!
private const int _TIME_PERIODIC = 1;
/// <summary>
/// The Event Handler for the timer
/// </summary>
private static TimerEventHandler _mHandler;
#endregion
#region Static Public Methods
/// <summary>
/// Start
/// </summary>
/// <param name="interval"> The interval between ticks in ms </param>
/// <param name="timerCallBack"> The delegate to call on each tick </param>
/// <returns> The Timer ID </returns>
public static UInt32 Start(uint interval, TimerEventHandler timerCallBack)
{
timeBeginPeriod(1);
_mHandler = timerCallBack;
return timeSetEvent(interval, 0, _mHandler, IntPtr.Zero, _EVENT_TYPE);
}
/// <summary>
/// Stop a timer with a given ID
/// </summary>
/// <param name="mTimerId"> The Timer ID of the timer to stop </param>
/// <returns> </returns>
public static bool Stop(UInt32 mTimerId)
{
UInt32 err = timeKillEvent(mTimerId);
if (err != (int) Mmresult.Mmsyserr_Noerror)
return false;
timeEndPeriod(1);
// Ensure callbacks are drained
Thread.Sleep(100);
return true;
}
#endregion
#region Static Private Methods
[DllImport("winmm.dll")]
private static extern int timeBeginPeriod(int msec);
[DllImport("winmm.dll")]
private static extern int timeEndPeriod(int msec);
/// <summary>
/// The multi media timer stop function
/// </summary>
/// <param name="uTimerId"> timer id from timeSetEvent </param>
/// <remarks>
/// This function stops the timer
/// </remarks>
[DllImport("winmm.dll", SetLastError = true)]
private static extern UInt32 timeKillEvent(UInt32 uTimerId);
/// <summary>
/// A multi media timer with millisecond precision
/// </summary>
/// <param name="msDelay"> One event every msDelay milliseconds </param>
/// <param name="msResolution"> Timer precision indication (lower value is more precise but resource unfriendly) </param>
/// <param name="handler"> delegate to start </param>
/// <param name="userCtx"> callBack data </param>
/// <param name="eventType"> one event or multiple events </param>
/// <remarks>
/// Dont forget to call timeKillEvent!
/// </remarks>
/// <returns> 0 on failure or any other value as a timer id to use for timeKillEvent </returns>
[DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeSetEvent")]
private static extern UInt32 timeSetEvent(UInt32 msDelay, UInt32 msResolution, TimerEventHandler handler,
IntPtr userCtx, UInt32 eventType);
#endregion
#region Public Delegates
/// <summary>
/// The Timer's Event Handler
/// </summary>
/// <param name="id"> </param>
/// <param name="msg"> </param>
/// <param name="user"> </param>
/// <param name="dw1"> </param>
/// <param name="dw2"> </param>
public delegate void TimerEventHandler(int id, int msg, IntPtr user, int dw1, int dw2);
#endregion
#region Public Enumerators
/// <summary>
/// Possible Return Values
/// </summary>
public enum Mmresult : uint
{
/// <summary>
/// No Error
/// </summary>
Mmsyserr_Noerror = 0,
/// <summary>
/// Error
/// </summary>
Mmsyserr_Error = 1,
/// <summary>
/// Bad Device ID
/// </summary>
Mmsyserr_Baddeviceid = 2,
/// <summary>
/// Not Enabled
/// </summary>
Mmsyserr_Notenabled = 3,
/// <summary>
/// Allocated
/// </summary>
Mmsyserr_Allocated = 4,
/// <summary>
/// Invalid Handle
/// </summary>
Mmsyserr_Invalhandle = 5,
/// <summary>
/// No Driver
/// </summary>
Mmsyserr_Nodriver = 6,
/// <summary>
/// No Mem
/// </summary>
Mmsyserr_Nomem = 7,
/// <summary>
/// Not Supported
/// </summary>
Mmsyserr_Notsupported = 8,
/// <summary>
/// Bad Error Number
/// </summary>
Mmsyserr_Baderrnum = 9,
/// <summary>
/// Invalid Falg
/// </summary>
Mmsyserr_Invalflag = 10,
/// <summary>
/// Invalid Parameter
/// </summary>
Mmsyserr_Invalparam = 11,
/// <summary>
/// Handle Busy
/// </summary>
Mmsyserr_Handlebusy = 12,
/// <summary>
/// Invalid Alias
/// </summary>
Mmsyserr_Invalidalias = 13,
/// <summary>
/// Baddb
/// </summary>
Mmsyserr_Baddb = 14,
/// <summary>
/// Key Not Found
/// </summary>
Mmsyserr_Keynotfound = 15,
/// <summary>
/// Read Error
/// </summary>
Mmsyserr_Readerror = 16,
/// <summary>
/// Write Error
/// </summary>
Mmsyserr_Writeerror = 17,
/// <summary>
/// Delete Error
/// </summary>
Mmsyserr_Deleteerror = 18,
/// <summary>
/// Value Not Found
/// </summary>
Mmsyserr_Valnotfound = 19,
/// <summary>
/// No Driver CB
/// </summary>
Mmsyserr_Nodrivercb = 20,
/// <summary>
/// Bad Format
/// </summary>
Waverr_Badformat = 32,
/// <summary>
/// Still Playing
/// </summary>
Waverr_Stillplaying = 33,
/// <summary>
/// Unprepared
/// </summary>
Waverr_Unprepared = 34
}
#endregion
}
#endregion
}
}
如何在Windows中实现高精度定时器,还有很多问题。。。首先进行搜索,然后编辑帖子以指定可接受的内容,即以80-120毫秒的间隔每秒拨打10次电话很容易,使用许多选项进行插值很容易,以100毫秒的间隔拨打10次电话非常困难
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace TimerTest.Timing.Timers
{
/// <summary>
/// A wrapper for a winmm.dll timer
/// </summary>
public class MultimediaTimer : AbstractTimer
{
#region Public Events
/// <summary>
/// Event Raised when the Timer has elapsed
/// </summary>
public override event EventHandler<TickEventArgs> Tick;
#endregion
#region Private Fields
private bool _enabled;
private readonly Form _form;
private double _intervalLength;
private int _intervalsElapsed;
private DateTime _startTime;
private UInt32 _timerId;
#endregion
#region Properties
public override bool Enabled
{
get { return _enabled; }
set
{
if (value)
{
Start();
}
else
{
Stop();
}
}
}
public override Form Form
{
get { return _form; }
}
public override double IntervalLength
{
get { return _intervalLength; }
set { _intervalLength = value; }
}
public override int IntervalsElapsed
{
get { return _intervalsElapsed; }
}
public override string Name
{
get { return "MultiMedia Timer"; }
}
public override DateTime StartTime
{
get { return _startTime; }
}
#endregion
#region Constructors
/// <summary>
/// Initializes an instance of MultimediaTimer
/// </summary>
/// <param name="form"> The form which the timer will invoke the Tick event on </param>
/// <param name="intervalLength"> The length of the interval in milliseconds </param>
public MultimediaTimer(Form form, double intervalLength)
{
_form = form;
_intervalLength = intervalLength;
}
#endregion
#region Public Methods
public override void Start()
{
if (Enabled)
return;
uint interval = Convert.ToUInt32(Math.Round(_intervalLength, 0));
_timerId = Winmm.Start(interval, TimerCallback);
_startTime = DateTime.Now;
_enabled = true;
}
public override bool Stop()
{
if (Enabled)
{
bool stopped = Winmm.Stop(_timerId);
_enabled = !stopped;
return stopped;
}
return false;
}
#endregion
#region Private Methods
private void TimerCallback(int id, int msg, IntPtr user, int dw1, int dw2)
{
_intervalsElapsed++;
if (Enabled)
{
TriggerTick();
}
}
private void TriggerTick()
{
DateTime currentTime = DateTime.Now;
_form.BeginInvoke((MethodInvoker) delegate
{
TickEventArgs tickEventArgs = new TickEventArgs(StartTime,
IntervalsElapsed,
IntervalLength,
currentTime);
if (Tick != null)
{
Tick(this, tickEventArgs);
}
});
}
#endregion
#region Nested Classes
/// <summary>
/// A static wrapper for Winmm.dll
/// </summary>
public static class Winmm
{
#region Static Private Fields
/// <summary>
/// The Event Type
/// </summary>
private const int _EVENT_TYPE = _TIME_PERIODIC; // + 0x100; // TIME_KILL_SYNCHRONOUS causes a hang ?!
private const int _TIME_PERIODIC = 1;
/// <summary>
/// The Event Handler for the timer
/// </summary>
private static TimerEventHandler _mHandler;
#endregion
#region Static Public Methods
/// <summary>
/// Start
/// </summary>
/// <param name="interval"> The interval between ticks in ms </param>
/// <param name="timerCallBack"> The delegate to call on each tick </param>
/// <returns> The Timer ID </returns>
public static UInt32 Start(uint interval, TimerEventHandler timerCallBack)
{
timeBeginPeriod(1);
_mHandler = timerCallBack;
return timeSetEvent(interval, 0, _mHandler, IntPtr.Zero, _EVENT_TYPE);
}
/// <summary>
/// Stop a timer with a given ID
/// </summary>
/// <param name="mTimerId"> The Timer ID of the timer to stop </param>
/// <returns> </returns>
public static bool Stop(UInt32 mTimerId)
{
UInt32 err = timeKillEvent(mTimerId);
if (err != (int) Mmresult.Mmsyserr_Noerror)
return false;
timeEndPeriod(1);
// Ensure callbacks are drained
Thread.Sleep(100);
return true;
}
#endregion
#region Static Private Methods
[DllImport("winmm.dll")]
private static extern int timeBeginPeriod(int msec);
[DllImport("winmm.dll")]
private static extern int timeEndPeriod(int msec);
/// <summary>
/// The multi media timer stop function
/// </summary>
/// <param name="uTimerId"> timer id from timeSetEvent </param>
/// <remarks>
/// This function stops the timer
/// </remarks>
[DllImport("winmm.dll", SetLastError = true)]
private static extern UInt32 timeKillEvent(UInt32 uTimerId);
/// <summary>
/// A multi media timer with millisecond precision
/// </summary>
/// <param name="msDelay"> One event every msDelay milliseconds </param>
/// <param name="msResolution"> Timer precision indication (lower value is more precise but resource unfriendly) </param>
/// <param name="handler"> delegate to start </param>
/// <param name="userCtx"> callBack data </param>
/// <param name="eventType"> one event or multiple events </param>
/// <remarks>
/// Dont forget to call timeKillEvent!
/// </remarks>
/// <returns> 0 on failure or any other value as a timer id to use for timeKillEvent </returns>
[DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeSetEvent")]
private static extern UInt32 timeSetEvent(UInt32 msDelay, UInt32 msResolution, TimerEventHandler handler,
IntPtr userCtx, UInt32 eventType);
#endregion
#region Public Delegates
/// <summary>
/// The Timer's Event Handler
/// </summary>
/// <param name="id"> </param>
/// <param name="msg"> </param>
/// <param name="user"> </param>
/// <param name="dw1"> </param>
/// <param name="dw2"> </param>
public delegate void TimerEventHandler(int id, int msg, IntPtr user, int dw1, int dw2);
#endregion
#region Public Enumerators
/// <summary>
/// Possible Return Values
/// </summary>
public enum Mmresult : uint
{
/// <summary>
/// No Error
/// </summary>
Mmsyserr_Noerror = 0,
/// <summary>
/// Error
/// </summary>
Mmsyserr_Error = 1,
/// <summary>
/// Bad Device ID
/// </summary>
Mmsyserr_Baddeviceid = 2,
/// <summary>
/// Not Enabled
/// </summary>
Mmsyserr_Notenabled = 3,
/// <summary>
/// Allocated
/// </summary>
Mmsyserr_Allocated = 4,
/// <summary>
/// Invalid Handle
/// </summary>
Mmsyserr_Invalhandle = 5,
/// <summary>
/// No Driver
/// </summary>
Mmsyserr_Nodriver = 6,
/// <summary>
/// No Mem
/// </summary>
Mmsyserr_Nomem = 7,
/// <summary>
/// Not Supported
/// </summary>
Mmsyserr_Notsupported = 8,
/// <summary>
/// Bad Error Number
/// </summary>
Mmsyserr_Baderrnum = 9,
/// <summary>
/// Invalid Falg
/// </summary>
Mmsyserr_Invalflag = 10,
/// <summary>
/// Invalid Parameter
/// </summary>
Mmsyserr_Invalparam = 11,
/// <summary>
/// Handle Busy
/// </summary>
Mmsyserr_Handlebusy = 12,
/// <summary>
/// Invalid Alias
/// </summary>
Mmsyserr_Invalidalias = 13,
/// <summary>
/// Baddb
/// </summary>
Mmsyserr_Baddb = 14,
/// <summary>
/// Key Not Found
/// </summary>
Mmsyserr_Keynotfound = 15,
/// <summary>
/// Read Error
/// </summary>
Mmsyserr_Readerror = 16,
/// <summary>
/// Write Error
/// </summary>
Mmsyserr_Writeerror = 17,
/// <summary>
/// Delete Error
/// </summary>
Mmsyserr_Deleteerror = 18,
/// <summary>
/// Value Not Found
/// </summary>
Mmsyserr_Valnotfound = 19,
/// <summary>
/// No Driver CB
/// </summary>
Mmsyserr_Nodrivercb = 20,
/// <summary>
/// Bad Format
/// </summary>
Waverr_Badformat = 32,
/// <summary>
/// Still Playing
/// </summary>
Waverr_Stillplaying = 33,
/// <summary>
/// Unprepared
/// </summary>
Waverr_Unprepared = 34
}
#endregion
}
#endregion
}
}