Windows 7引入了计时器合并,提高了能效。什么托管API暴露计时器容差?似乎利用此功能的唯一方法是p / invoke SetWaitableTimerEx。
答案 0 :(得分:5)
没有我知道的托管API,但是说,这是一个不那么毛茸茸的P / Invokes之一 - 这是一个快速而又脏的类,我只是简单的用法:< / p>
(我应该注意到我只是非常基本上测试了这个...可能需要一些调整) [编辑:好的,有机会在午餐时调整一下,这应该工作,或多或少]
void Main()
{
var waitFor = 6000;
var tickAt = 2000;
var tickEvery = 1000;
var sw = Stopwatch.StartNew();
var running = true;
var apcTask = Task.Factory.StartNew(() =>
{
try
{
Console.WriteLine("APC:Creating timer...");
ApcTimer timer = new ApcTimer(@"Global\WillThisWork", tickAt, tickEvery, true);
timer.Tick += (o,e) =>
{
Console.WriteLine("APC:Hey, it worked! - delta:{0}", sw.Elapsed);
};
Console.WriteLine("APC:Starting timer...");
timer.Start();
while(running);
Console.WriteLine("APC:Stopping timer...");
timer.Dispose();
Console.WriteLine("APC:Finishing - delta:{0}", sw.Elapsed);
}
catch(Exception ex)
{
Console.WriteLine(ex);
}
});
Thread.Sleep(waitFor);
running = false;
Task.WaitAll(apcTask);
}
public class ApcTimer : IDisposable
{
public delegate void TimerApcCallback(object sender, EventArgs args);
public event TimerApcCallback Tick;
private const long _MILLISECOND = 10000;
private const long _SECOND = 10000000;
private IntPtr _hTimer = IntPtr.Zero;
private long _delayInMs;
private int _period;
private bool _resumeFromSleep;
private Task _alerter;
private CancellationTokenSource _cancelSource;
private bool _timerRunning;
public ApcTimer(
string timerName,
long delayInMs,
int period,
bool resumeFromSleep)
{
_hTimer = CreateWaitableTimer(IntPtr.Zero, false,timerName);
if(_hTimer == IntPtr.Zero)
{
// This'll grab the last win32 error nicely
throw new System.ComponentModel.Win32Exception();
}
_delayInMs = delayInMs;
_period = period;
_resumeFromSleep = resumeFromSleep;
}
public void Start()
{
var sw = Stopwatch.StartNew();
Debug.WriteLine("ApcTimer::Starting timer...");
StopAlerter();
SetTimer(_delayInMs);
_cancelSource = new CancellationTokenSource();
_alerter = Task.Factory.StartNew(
()=>
{
_timerRunning = true;
while(_timerRunning)
{
var res = WaitForSingleObject(_hTimer, -1);
if(res == WaitForResult.WAIT_OBJECT_0)
{
if(Tick != null)
{
Tick.Invoke(this, new EventArgs());
}
SetTimer(_period);
}
}
}, _cancelSource.Token);
Debug.WriteLine("ApcTimer::Started!");
}
public void Dispose()
{
Debug.WriteLine("ApcTimer::Stopping timer...");
StopAlerter();
CancelPendingTimer();
if(_hTimer != IntPtr.Zero)
{
var closeSucc = CloseHandle(_hTimer);
if(!closeSucc)
{
throw new System.ComponentModel.Win32Exception();
}
_hTimer = IntPtr.Zero;
}
Debug.WriteLine("ApcTimer::Stopped!");
}
private void SetTimer(long waitMs)
{
// timer delay is normally in 100 ns increments
var delayInBlocks = new LARGE_INTEGER() { QuadPart = (waitMs * _MILLISECOND * -1)};
bool setSucc = false;
setSucc = SetWaitableTimer(_hTimer, ref delayInBlocks, 0, IntPtr.Zero, IntPtr.Zero, _resumeFromSleep);
if(!setSucc)
{
// This'll grab the last win32 error nicely
throw new System.ComponentModel.Win32Exception();
}
}
private void CancelPendingTimer()
{
if(_hTimer != IntPtr.Zero)
{
Debug.WriteLine("ApcTimer::Cancelling pending timer...");
CancelWaitableTimer(_hTimer);
}
}
private void StopAlerter()
{
_timerRunning = false;
if(_alerter != null)
{
Debug.WriteLine("ApcTimer::Shutting down alerter...");
_cancelSource.Cancel();
Task.WaitAll(_alerter);
}
}
#region secret pinvoke goodness
[DllImport("Kernel32.dll", SetLastError=true)]
static extern WaitForResult WaitForSingleObject([In] IntPtr hHandle, int dwMilliseconds);
[DllImport("Kernel32.dll", SetLastError=true)]
[return:MarshalAs(UnmanagedType.Bool)]
static extern bool CancelWaitableTimer([In] IntPtr hTimer);
[DllImport("Kernel32.dll", SetLastError=true)]
[return:MarshalAs(UnmanagedType.Bool)]
static extern bool SetWaitableTimer(
[In] IntPtr hTimer,
[In] ref LARGE_INTEGER dueTime,
[In] int period,
[In] IntPtr completionRoutine,
[In] IntPtr argToCallback,
[In] bool resume);
[DllImport("Kernel32.dll", SetLastError=true)]
static extern IntPtr CreateWaitableTimer(
IntPtr securityAttributes,
bool manualReset,
string timerName);
[DllImport("Kernel32.dll", SetLastError=true)]
static extern IntPtr CreateWaitableTimerEx(
IntPtr securityAttributes,
string timerName,
TimerCreateFlags flags,
TimerAccessFlags desiredAccess);
[DllImport("Kernel32.dll", SetLastError=true)]
[return:MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr handle);
private const int INFINITE_TIMEOUT = 1;
[Flags]
private enum WaitForResult : int
{
WAIT_ABANDONED = 0x00000080,
WAIT_OBJECT_0 = 0,
WAIT_TIMEOUT = 0x00000102,
WAIT_FAILED = -1
}
[Flags]
private enum TimerAccessFlags : int
{
TIMER_ALL_ACCESS = 0x1F0003,
TIMER_MODIFY_STATE = 0x0002,
TIMER_QUERY_STATE = 0x0001
}
[Flags]
private enum TimerCreateFlags : int
{
CREATE_WAITABLE_TIMER_MANUAL_RESET = 0x00000001
}
[StructLayout(LayoutKind.Sequential)]
public struct LargeIntegerSplitPart
{
public uint LowPart;
public int HighPart;
}
[StructLayout(LayoutKind.Explicit)]
public struct LARGE_INTEGER
{
[FieldOffset(0)]
public LargeIntegerSplitPart u;
[FieldOffset(0)]
public long QuadPart;
}
#endregion
}