Подтвердить что ты не робот

Беспроволочный бесконечный цикл

Запись бесконечного цикла проста:

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

Но это приведет к сбою производительности ЦП. Этот поток выполнения будет как можно больше от мощности процессора.

Каков наилучший способ снизить влияние на процессор? Добавление некоторого Thread.Sleep(n) должно выполнить трюк, но установка значения высокого тайм-аута для метода Sleep() может указывать на невосприимчивое приложение к операционной системе.

Скажем, мне нужно выполнить задачу каждую минуту или около того в консольном приложении. Мне нужно, чтобы Main() работал в "бесконечном цикле", в то время как таймер запускает событие, которое будет выполнять задание. Я хотел бы сохранить Main() с наименьшим воздействием на CPU.

Какие методы вы предлагаете. Sleep() может быть в порядке, но, как я уже упоминал, это может указывать на невосприимчивый поток к операционной системе.

LATER EDIT:

Я хочу лучше объяснить, что я ищу:

  • Мне нужно консольное приложение, а не служба Windows. Консольные приложения могут имитировать службы Windows в системах Windows Mobile 6.x с Compact Framework.

  • Мне нужно, чтобы приложение поддерживалось до тех пор, пока работает устройство Windows Mobile.

  • Мы все знаем, что консольное приложение работает до тех пор, пока его статическая функция Main() запускается, поэтому мне нужен способ предотвращения выхода Main().

  • В особых ситуациях (например: обновление приложения) мне нужно попросить приложение остановиться, поэтому мне нужно бесконечно контактировать и проверить какое-то условие выхода. Например, поэтому Console.ReadLine() мне не подходит. Нет проверки состояния выхода.

  • Что касается вышеизложенного, я по-прежнему хочу, чтобы функция Main() была как можно более дружественной к ресурсам. Пусть проверяется отпечаток функции, которая проверяет условие выхода.

4b9b3361

Ответ 1

Чтобы избежать цикла бесконечности, просто используйте WaitHandle. Чтобы позволить процессу выйти из внешнего мира, используйте EventWaitHandle с уникальной строкой. Ниже приведен пример.

Если вы запустите его в первый раз, он просто распечатает сообщение каждые 10 секунд. Если вы запустите в среднем время второго экземпляра программы, он сообщит другому процессу, чтобы изящно выйти и выйти сразу же. Использование ЦП для этого подхода: 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.");
}

Ответ 2

Вы можете использовать System.Threading.Timer Класс, который обеспечивает возможность выполнения обратного вызова асинхронно в заданный период времени.

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

В качестве альтернативы существует System.Timers.Timer класс, который предоставляет Elapsed Event, который увеличивается, когда истекает заданный период времени.

Ответ 3

Почему вы потворствуете использованию бесконечного цикла? В этом примере установка программы в качестве запланированной задачи должна выполняться каждую минуту, не более экономичной?

Ответ 4

Почему бы вам не написать небольшое приложение и использовать планировщик системных задач для запуска его каждую минуту, час... и т.д.?

Другой вариант - написать службу Windows, которая работает в фоновом режиме. Служба может использовать простой класс Alarm, такой как следующий в MSDN:

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 для выполнения вашей задачи. Не нужно внедрять "бесконечный цикл", чтобы поддерживать приложение в ожидании. Это выполняется для вас службой.

Ответ 5

Мне кажется, что вы хотите, чтобы Main() вводил прерывистый цикл. Чтобы это произошло, нужно задействовать несколько потоков (или ваш цикл должен периодически регистрироваться, но я здесь не обсуждаю это решение). Любой другой поток в одном приложении или поток в другом процессе должен иметь возможность сигнализировать о своем цикле 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");
    }
}

Ответ 6

Вы можете использовать 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% одного ядра на моей машине.

Ответ 7

Изложить текст комментария CodeInChaos:

Вы можете установить заданный приоритет потока. Потоки запланированы для выполнения в зависимости от их приоритета. Алгоритм планирования, используемый для определения порядка выполнения потоков, зависит от каждой операционной системы. Все потоки по умолчанию равны "нормальному" приоритету, но если вы установите свой цикл на низкий; он не должен крать время из потоков, установленных в нормальное состояние.

Ответ 8

Я сделал это для приложения, которое должно было обрабатывать файлы по мере их удаления в папке. Лучше всего использовать таймер (как предлагается) с Console.ReadLine() в конце "main", не вставляя петлю.

Теперь ваше беспокойство о том, чтобы остановить приложение:

Я также сделал это через некоторый рудиментарный "файловый" монитор. Просто создав файл "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);
}

Теперь вы упомянули, что это (или может быть) для мобильного приложения? У вас может не быть наблюдателя файловой системы. В этом случае, возможно, вам просто нужно "убить" процесс (вы сказали: "В особых ситуациях (например: обновление приложения) мне нужно попросить приложение остановить". Кто бы ни "запрашивающий", чтобы остановить его, должен просто убить процесс)

Ответ 9

Подход Timer, вероятно, будет вашим лучшим выбором, но поскольку вы упоминаете Thread.Sleep, есть интересный Thread.SpinWait или SpinWait struct альтернатива для аналогичных проблем, которые иногда могут быть лучше, чем короткие Thread.Sleep invocations.

Также см. этот вопрос: Какова цель метода Thread.SpinWait?

Ответ 10

Здесь много "расширенных" ответов, но IMO просто использует Thread.Sleep(lowvalue) для большинства.

Таймеры также являются решением, но код за таймером также представляет собой цикл бесконечности - я бы предположил, что он запускает ваш код с истекшими интервалами, но у них есть правильная настройка бесконечности.

Если вам нужен большой сон, вы можете разрезать его на более мелкие соны.

Итак, что-то вроде простого и легкого 0% -ного решения для процессора для приложения, отличного от 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-код. Нить спать в пользовательском интерфейсе будет легко обнаружена. Приложение "Приложение не отвечает" использует простой собственный метод "SendMessageTimeout", чтобы узнать, есть ли в приложении пользовательский интерфейс ответа на запрос.

Любой цикл бесконечности в приложении UI должен всегда запускаться в отдельном потоке.

Ответ 11

Чтобы консольные приложения работали, просто добавьте Console.ReadLine() в конец вашего кода в Main().

Если пользователь не сможет завершить работу приложения, вы можете сделать это с помощью следующего цикла:

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