CPU友好的无限循环

写一个无限循环很简单:

while(true){ //add whatever break condition here } 

但是这会损害CPU的性能。 这个执行线程将尽可能地从CPU的力量。

降低对CPU的影响最好的办法是什么? 添加一些Thread.Sleep(n)应该可以做到这一点,但是为Sleep()方法设置一个很高的超时值可能表示对操作系统没有反应的应用程序。

假设我需要在控制台应用程序中每分钟执行一次任务。 我需要保持Main()在“无限循环”中运行,而计时器将触发将执行此任务的事件。 我想保持Main()对CPU的影响最小。

你build议什么方法。 Sleep()可以,但是正如我已经提到的,这可能表示对操作系统没有响应的线程。

后期编辑:

我想更好地解释我在找什么:

  1. 我需要一个控制台应用程序不Windows服务。 控制台应用程序可以在Compact Framework上模拟Windows Mobile 6.x系统上的Windows服务。

  2. 只要Windows Mobile设备正在运行,我需要一种方法来保持应用程序的活跃。

  3. 我们都知道,只要静态Main()函数运行,控制台应用程序就会运行,所以我需要一种防止Main()函数退出的方法。

  4. 在特殊情况下(如:更新应用程序),我需要请求应用程序停止,所以我需要无限循环和testing一些退出条件。 例如,这就是为什么Console.ReadLine()对我没有用处。 没有退出条件检查。

  5. 关于上面,我仍然希望Main()函数尽可能的资源友好。 辅助检查退出条件的function的指纹。

为了避免无限循环,只需使用WaitHandle 。 要让进程从外部世界退出,请使用具有唯一string的EventWaitHandle 。 下面是一个例子。

如果您是第一次启动它,那么每10秒钟就会打印一条消息。 如果同时启动程序的第二个实例,它将通知另一个进程正常退出并立即退出。 这种方法的CPU使用率:0%

 private static void Main(string[] args) { // Create a IPC wait handle with a unique identifier. bool createdNew; var waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, "CF2D4313-33DE-489D-9721-6AFF69841DEA", out createdNew); var signaled = false; // If the handle was already there, inform the other process to exit itself. // Afterwards we'll also die. if (!createdNew) { Log("Inform other process to stop."); waitHandle.Set(); Log("Informer exited."); return; } // Start a another thread that does something every 10 seconds. var timer = new Timer(OnTimerElapsed, null, TimeSpan.Zero, TimeSpan.FromSeconds(10)); // Wait if someone tells us to die or do every five seconds something else. do { signaled = waitHandle.WaitOne(TimeSpan.FromSeconds(5)); // ToDo: Something else if desired. } while (!signaled); // The above loop with an interceptor could also be replaced by an endless waiter //waitHandle.WaitOne(); Log("Got signal to kill myself."); } private static void Log(string message) { Console.WriteLine(DateTime.Now + ": " + message); } private static void OnTimerElapsed(object state) { Log("Timer elapsed."); } 

您可以使用System.Threading.Timer类,它可以在给定的时间段内asynchronous执行callback。

 public Timer( TimerCallback callback, Object state, int dueTime, int period ) 

另外还有System.Timers.Timer类,它暴露了在经过一段时间后引发的Elapsed事件 。

你为什么会宽容使用无限循环? 对于这个例子,将程序设置为计划任务,每分钟运行一次,而不是更经济?

你为什么不写一个小应用程序,并使用系统的任务调度程序来运行它每分钟,小时…等?

另一种select是编写一个在后台运行的Windows服务。 该服务可以在MSDN上使用如下简单的Alarm类:

http://msdn.microsoft.com/en-us/library/wkzf914z%28v=VS.90%29.aspx#Y2400

您可以使用它来定期触发您的方法。 内部这个报警类使用一个计时器:

http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx

只要正确设置定时器的时间间隔(例如60000毫秒),它就会定期提高Elapsed事件。 将事件处理程序附加到Elapsed事件以执行您的任务。 没有必要实施一个“无限循环”只是为了保持应用程序的活着。 这是由服务处理你。

这听起来像你想要Main()进入一个可中断的循环。 要发生这种情况,必须在某个地方涉及多个线程(或者,您的循环必须定期轮询;不过我不讨论这个解决scheme)。 另一个线程在同一个应用程序或另一个进程中的一个线程必须能够发信号给你的Main()循环,它应该终止。

如果这是真的,那么我想你想使用ManualResetEvent或EventWaitHandle 。 您可以等待该事件,直到发出信号(并且信号必须由另一个线程完成)。

例如:

 using System; using System.Threading; using System.Threading.Tasks; namespace Demo { class Program { static void Main(string[] args) { startThreadThatSignalsTerminatorAfterSomeTime(); Console.WriteLine("Waiting for terminator to be signalled."); waitForTerminatorToBeSignalled(); Console.WriteLine("Finished waiting."); Console.ReadLine(); } private static void waitForTerminatorToBeSignalled() { _terminator.WaitOne(); // Waits forever, but you can specify a timeout if needed. } private static void startThreadThatSignalsTerminatorAfterSomeTime() { // Instead of this thread signalling the event, a thread in a completely // different process could do so. Task.Factory.StartNew(() => { Thread.Sleep(5000); _terminator.Set(); }); } // I'm using an EventWaitHandle rather than a ManualResetEvent because that can be named and therefore // used by threads in a different process. For intra-process use you can use a ManualResetEvent, which // uses slightly fewer resources and so may be a better choice. static readonly EventWaitHandle _terminator = new EventWaitHandle(false, EventResetMode.ManualReset, "MyEventName"); } } 

您可以使用Begin-/End-Invoke来产生其他线程。 例如

 public static void ExecuteAsyncLoop(Func<bool> loopBody) { loopBody.BeginInvoke(ExecuteAsyncLoop, loopBody); } private static void ExecuteAsyncLoop(IAsyncResult result) { var func = ((Func<bool>)result.AsyncState); try { if (!func.EndInvoke(result)) return; } catch { // Do something with exception. return; } func.BeginInvoke(ExecuteAsyncLoop, func); } 

你会这样使用它:

 ExecuteAsyncLoop(() => { // Do something. return true; // Loop indefinitely. }); 

这使用了我的机器上一个核心的60%(完全空的循环)。 或者,你可以在你的循环体中使用这个( 源代码):

 private static readonly bool IsSingleCpuMachine = (Environment.ProcessorCount == 1); [DllImport("kernel32", ExactSpelling = true)] private static extern void SwitchToThread(); private static void StallThread() { // On a single-CPU system, spinning does no good if (IsSingleCpuMachine) SwitchToThread(); // Multi-CPU system might be hyper-threaded, let other thread run else Thread.SpinWait(1); } while (true) { // Do something. StallThread(); } 

在我的机器上使用了一个核心的20%。

解释CodeInChaos提出的评论:

您可以设置给定线程的优先级 。 线程根据其优先级安排执行。 用于确定线程执行顺序的调度algorithm因操作系统而异。 所有的线程默认为“正常”优先,但如果你设置你的循环为低; 它不应该从设置为正常的线程偷走时间。

我这样做是为了处理文件,因为它们被放在一个文件夹上。 你最好的select是一个计时器(build议)与“主”的末尾的Console.ReadLine(),而不是一个循环。

现在,你关心告诉应用程序停止:

我也通过一些基本的“文件”监视器做到了这一点。 只需在应用程序的根文件夹(通过我的程序或其他可能请求停止的应用程序)中创build“quit.txt”文件,就可以退出应用程序。 半代码:

 <do your timer thing here> watcher = new FileSystemWatcher(); watcher.Path = <path of your application or other known accessible path>; watcher.Changed += new FileSystemEventHandler(OnNewFile); Console.ReadLine(); 

OnNewFile可能是这样的:

 private static void OnNewFile(object source, FileSystemEventArgs e) { if(System.IO.Path.GetFileName(e.FullPath)).ToLower()=="quit.txt") ... remove current quit.txt Environment.Exit(1); } 

现在你提到,这是(或可能)为移动应用程序? 您可能没有文件系统监视器。 在这种情况下,也许你只需要“杀死”进程(你说“在特殊情况下(比如:更新应用程序),我需要请求应用程序停止”。“请求者”停止它,应该简单地杀死进程)

Timer方法可能是你最好的select,但是因为你提到了Thread.Sleep,所以有一个有趣的Thread.SpinWait或者SpinWait结构可以替代类似的问题,有时可能比简短的Thread.Sleep调用更好。

也看到这个问题: Thread.SpinWait方法的目的是什么?

很多“先进的”答案在这里,但国际海事组织只是使用Thread.Sleep(lowvalue)应该足够多。

定时器也是一个解决scheme,但是定时器后面的代码也是一个无限循环 – 我假设 – 在经过的时间间隔内触发你的代码,但是它们有正确的无限循环设置。

如果你需要大量的睡眠,你可以把它切成更小的睡眠。

所以像这样的东西是非UI应用程序的简单和容易的0%的CPU解决scheme。

 static void Main(string[] args) { bool wait = true; int sleepLen = 1 * 60 * 1000; // 1 minute while (wait) { //... your code var sleepCount = sleepLen / 100; for (int i = 0; i < sleepCount; i++) { Thread.Sleep(100); } } } 

关于操作系统如何检测应用程序是否无响应。 我不知道任何其他testing比在UI应用程序,其中有方法来检查UI线程是否处理UI代码。 线程睡在用户界面将很容易被发现。 Windows“应用程序无响应”使用简单的本机方法“SendMessageTimeout”来查看检测应用程序是否有未响应的用户界面。

UI应用程序的任何无限循环应始终在单独的线程中运行。

为了保持控制台应用程序的运行,只需在Main()中的代码末尾添加一个Console.ReadLine() Main()

如果用户不能终止应用程序,可以用下面的循环来完成:

 while (true){ Console.ReadLine(); }