it-swarm.cn

CPU友好的无限循环

编写无限循环很简单:

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

但这会破坏CPU的性能。此执行线程将尽可能多地从CPU的电源中获取。

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

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

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

LATER EDIT:

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

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

  2. 只要Windows Mobile设备运行,我需要一种方法让应用程序保持活动状态。

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

  4. 在特殊情况下(例如:更新应用程序),我需要请求应用程序停止,因此我需要无限循环并测试某些退出条件。例如,这就是Console.ReadLine()对我没用的原因。没有退出条件检查。

  5. 关于上面,我仍然希望Main()函数尽可能地对资源友好。让我们确定检查退出条件的函数的指纹。

51
Adi

要避免无限循环,只需使用WaitHandle。要让流程退出外部世界,请使用带有唯一字符串的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.");
}
51
Oliver

您可以使用 System.Threading.Timer Class,它提供在给定时间段内异步执行回调的功能。

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

作为替代方案,有 System.Timers.Timer class公开 Elapsed Event 在经过一段给定时间后上升。

9
sll

听起来像你想要Main()进入一个可中断的循环。为此,必须在某处涉及多个线程(或者您的循环必须定期轮询;但我不在此处讨论该解决方案)。同一应用程序中的另一个线程或另一个进程中的线程必须能够向Main()循环发出信号,告知它应该终止。

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

例如:

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");
    }
}
1
Matthew Watson

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

1
anothershrubery

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

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

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

您可以使用它来定期触发您的方法。在内部,此Alarm类使用计时器:

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

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

1
Christophe Geers

Timer方法可能是你最好的选择,但是因为你提到Thread.Sleep有一个有趣的 Thread.SpinWaitSpinWait struct 替代类似的问题,有时可能比短线程更好。睡眠调用。

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

0
Zaid Masud

为了阐述CodeInChaos的评论:

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

0
Tremmors

这里有很多“高级”答案,但IMO只需使用Thread.Sleep(低值)就足够了。

定时器也是一个解决方案,但定时器背后的代码也是一个无限循环 - 我会假设 - 在经过的时间间隔内激活你的代码,但它们具有正确的无限循环设置。

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

所以像这样的东西是一个简单易用的0%CPU解决方案,适用于非UI应用。

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);
        }
    }
}

关于操作系统如何检测应用程序是否无响应。我不知道任何其他测试而不是UI应用程序,其中有方法来检查UI线程是否处理UI代码。很容易发现UI上的线程休眠。 Windows“应用程序无响应”使用简单的本机方法“SendMessageTimeout”来检测应用程序是否具有无响应UI。

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

0
Wolf5

我为一个必须处理文件的应用程序执行此操作,因为它们被放在文件夹上。你最好的选择是一个计时器(如建议的那样),在“main”的末尾有一个Console.ReadLine()而不需要循环。

现在,您关注告诉应用程序停止:

我也通过一些基本的“文件”监视器完成了这项工作。只需在应用程序的根文件夹中创建文件“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);
}

现在你提到这是(或可能)移动应用程序?您可能没有文件系统观察程序。在这种情况下,也许你只需要“杀死”这个过程(你说“在特殊情况下(比如:更新应用程序),我需要请求应用程序停止”。无论“请求者”阻止它的是谁,都应该干掉这个过程)

0
Nelson Rodriguez

您可以使用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%(完全空循环)。或者,您可以在循环体中使用此( Source )代码:

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%的一个核心。

0
Jonathan Dickinson