我希望在我的应用程序中触发一个事件,该事件在某一时间持续运行,例如在下午4:00。我想过每秒运行一次计时器,当时间等于下午4点时,运行该事件。这样可行。但是我想知道是否有办法在下午4点获得回调,而不必继续检查。
答案 0 :(得分:42)
var t = new Timer(TimerCallback);
// Figure how much time until 4:00
DateTime now = DateTime.Now;
DateTime fourOClock = DateTime.Today.AddHours(16.0);
// If it's already past 4:00, wait until 4:00 tomorrow
if (now > fourOClock)
{
fourOClock = fourOClock.AddDays(1.0);
}
int msUntilFour = (int)((fourOClock - now).TotalMilliseconds);
// Set the timer to elapse only once, at 4:00.
t.Change(msUntilFour, Timeout.Infinite);
请注意,如果使用System.Threading.Timer
,则TimerCallback
指定的回调将在线程池(非UI)线程上执行 - 因此,如果您计划对UI执行某些操作在4:00,你必须适当地编组代码(例如,在Windows窗体应用程序中使用Control.Invoke
,或在WPF应用程序中使用Dispatcher.Invoke
。
答案 1 :(得分:27)
从.NET 4.5开始,有一个非常干净的解决方案:
public async void ScheduleAction(Action action, DateTime ExecutionTime)
{
await Task.Delay((int)ExecutionTime.Subtract(DateTime.Now).TotalMilliseconds);
action();
}
这是一个没有async / await的解决方案:
public void Execute(Action action, DateTime ExecutionTime)
{
Task WaitTask = Task.Delay((int)ExecutionTime.Subtract(DateTime.Now).TotalMilliseconds);
WaitTask.ContinueWith(_ => action);
WaitTask.Start();
}
应该注意的是,由于int32最大值,这仅适用于大约24天,这对于您的情况很有用,但值得注意。
答案 2 :(得分:6)
您可以使用Task Sceduler on windows有关详细信息,请参阅daily trigger example。
如果您想自己编写,请使用以下代码:
public void InitTimer()
{
DateTime time = DateTime.Now;
int second = time.Second;
int minute = time.Minute;
if (second != 0)
{
minute = minute > 0 ? minute-- : 59;
}
if (minute == 0 && second == 0)
{
// DoAction: in this function also set your timer interval to 24 hours
}
else
{
TimeSpan span = //new daily timespan, previous code was hourly: new TimeSpan(0, 60 - minute, 60 - second);
timer.Interval = (int) span.TotalMilliseconds - 100;
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
}
void timer_Tick(object sender, EventArgs e)
{
timer.Interval = ...; // 24 hours
// DoAction
}
答案 3 :(得分:4)
以VoteCoffees为首,这是一个基于紧凑事件的解决方案:
public class DailyTrigger
{
readonly TimeSpan triggerHour;
public DailyTrigger(int hour, int minute = 0, int second = 0)
{
triggerHour = new TimeSpan(hour, minute, second);
InitiateAsync();
}
async void InitiateAsync()
{
while (true)
{
var triggerTime = DateTime.Today + triggerHour - DateTime.Now;
if (triggerTime < TimeSpan.Zero)
triggerTime = triggerTime.Add(new TimeSpan(24,0,0));
await Task.Delay(triggerTime);
OnTimeTriggered?.Invoke();
}
}
public event Action OnTimeTriggered;
}
消费者:`
void Main()
{
var trigger = new DailyTrigger(16); // every day at 4:00pm
trigger.OnTimeTriggered += () =>
{
// Whatever
};
Console.ReadKey();
}
答案 4 :(得分:0)
任务计划程序是一个更好的选择,它可以很容易地在C#中使用,http://taskscheduler.codeplex.com/
答案 5 :(得分:0)
我这样做是每天早上7点开火
bool _ran = false; //initial setting at start up
private void timer_Tick(object sender, EventArgs e)
{
if (DateTime.Now.Hour == 7 && _ran==false)
{
_ran = true;
Do_Something();
}
if(DateTime.Now.Hour != 7 && _ran == true)
{
_ran = false;
}
}
答案 6 :(得分:0)
回应Dan的解决方案,使用Timercallback是一个快速而简洁的解决方案。 在要安排要运行的任务或子例程的方法内,请使用以下命令:
t = New Timer(Sub()
'method call or code here'
End Sub, Nothing, 400, Timeout.Infinite)
使用&#39; Timeout.Infinite&#39;将确保回调仅在400ms后执行一次。我正在使用VB.Net。
答案 7 :(得分:0)
这个解决方案怎么样?
Sub Main()
Dim t As New Thread(AddressOf myTask)
t.Start()
Console.ReadLine()
End Sub
Private Sub myTask()
Dim a = "14:35"
Dim format = "dd/MM/yyyy HH:mm:ss"
Dim targetTime = DateTime.Parse(a)
Dim currentTime = DateTime.Parse(Now.ToString(format))
Console.WriteLine(currentTime)
Console.WriteLine("target time " & targetTime)
Dim bb As TimeSpan = targetTime - currentTime
If bb.TotalMilliseconds < 0 Then
targetTime = targetTime.AddDays(1)
bb = targetTime - currentTime
End If
Console.WriteLine("Going to sleep at " & Now.ToString & " for " & bb.TotalMilliseconds)
Thread.Sleep(bb.TotalMilliseconds)
Console.WriteLine("Woke up at " & Now.ToString(format))
End Sub
答案 8 :(得分:0)
.NET有很多计时器类,但是它们都占用相对于当前时间的时间跨度。在相对时间里,在启动计时器之前以及在计时器运行期间进行监视时,需要考虑很多事情。
操作系统非常适合处理这种复杂性。在.NET上运行的应用程序代码不是。
对于Windows,NuGet软件包AbsoluteTimer包装了一个在绝对时间到期的操作系统计时器。