Comparing DateTime's Isn't Working
I'm making a program that logs the processes of a computer and then from that initial time, every X minutes after, another log is saved in a 开发者_如何学编程text file. I've got the writing to the text file and everything going fine. I just can't seem to get DateTime to work properly or I'm misunderstanding its uses.
DateTime firstTime = DateTime.Now;
Process[] processlist = Process.GetProcesses();
foreach (Process theprocess in processlist)
{
sw.WriteLine("Process: {0} ID: {1}", theprocess.ProcessName, theprocess.Id);
}
sw.WriteLine(firstTime);
DateTime now = DateTime.Now.AddMinutes(1);
sw.WriteLine(now);
while (DateTime.Now == DateTime.Now)
{
DateTime nuw = DateTime.Now;
sw.WriteLine(nuw);
if (nuw == now)
{
sw.WriteLine("It worked");
}
}
I know the while loop is terrible, but I was just seeing if anything could work. The problem that I think is causing this is that DateTime.Now, doesn't update/change every second, it remains the same throughout the entire duration. Even going console and writing it out endlessly, it remains the same date that was first called.
So yea, I'm at the end of what I know what to do.
You should use a timer that fires every X minutes to achieve your task. Your while loop will lock the UI of your program and eat a lot of CPU.
The comparision ==
on DateTime
compares the internal ticks. A tick is 100 ns or one ten-millionth of a second, there are 10,000 ticks in a millisecond. In constrast, windows runs with a cycle time of about 20ms. The comparision is like trying to find a needle in a haystack the size of the universe.
Use (nuw >= now)
instead, it compares for exact match or after now.
The System.Threading.Timer class should do the trick. You will have to provide a using for the System.Threading namespace.
static public void ListProcesses(Object stateInfo)
{
Process[] processlist = Process.GetProcesses();
foreach (Process theprocess in processlist)
{
Console.WriteLine("Process: {0} ID: {1}", theprocess.ProcessName, theprocess.Id);
}
}
static void Main(string[] args)
{
//create a callback to our worker method
TimerCallback callback = new TimerCallback(ListProcesses);
// create a one minute timer tick
Timer stateTimer = new Timer(callback, null, 0, 60000);
// loop here forever
while (true)
{
//sleep every second so we dont hog the cpu
Thread.Sleep(1000);
//add program control logic
//if we want to exit the program with anything other than closing the window
}
}
you should try something like
if(now.CompareTo(nuw) < 0)
Comparing for equality on time is kind of like trying to split an arrow in archery. Getting exactly one minute out isn't likely.
I think you want if (nuw >= now)
Ok from my perspective the biggest thing you're causing yourself problems with is your variable naming, unless i'm missing something - always use clear variable names so that your code reads out naturally.
The use of a timer as shown above is certainly an excellent way of solving your problem, however i'll continue and keep in with the theme of the code you've written since the author of the timer answer has covered that one very well.
I must admit also tend to be very shy of "while(true)" loops as i'd rather have a more proactive control of stopping the app, usually via Start() and Stop() methods and an _isRunning variable - where the _isRunning is what goes in as your loop condition. For simplicity however i'll stick with a while(true).
So, rewritten slightly, your loop would look like this:
DateTime timeOfNextOperation = DateTime.Now.AddMinutes(1);
Console.WriteLine(timeOfNextOperation);
while (true)
{
if (DateTime.Now >= timeOfNextOperation)
{
Console.WriteLine("Do Stuff Here");
timeOfNextOperation = DateTime.Now.AddMinutes(1);
}
Thread.Sleep(1000);
}
You absolutely must put some kind of yield (a sleep of some kind) in to your loop or you will use 100% cpu in a tight loop. Even a sleep of 1ms makes a huge difference but think hard about whether you really need it to spin that fast - in this case, it certainly doesn't, you're only doing something once every minute spin every second is more than adequate.
Your loop also did not reset it to make it happen every minute, the above does, by setting the time of the next operation. Note that in this example i'm setting it AFTER i've done my task (in this case, print a line :) ) - this means that it will run with a 1 minute gap between each execution, NOT every minute - i.e. if the task takes 30 seconds then it will happen at 1 minute, then 2 minutes 30 seconds. If you put the task AFTER then it will schedule to run 1 minute after it starts running, so it will run at 1min then 2min, the danger is that if your task ever takes more than 1 minute then it will spin straight back in to it again.
As a general rule, never compare just with the end of a range, always compare with <= or >= - in some cases it's not necessary (eg. if you're stepping through a loop one at a time you're not likely to miss the last one) however it is a good habit to get in to so that you can't end up with cases where you didn't realise it could skip one.
All the above said I do agree that the Timer is your best bet, the only optimisation i'd make to infomaniac's excellent code sample is that you don't need to create the delegate reference, it's implicit in the newer versions of .NET .
i.e.
Timer stateTimer = new Timer(ListProcesses, null, 0, 60000);
Hope that helps.
精彩评论