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

Лучшая практика для бесконечного/периодического выполнения кода в С#

Часто в моем коде я запускаю угрозы, которые в основном выглядят следующим образом:

void WatchForSomething()
{
    while(true)
    {
        if(SomeCondition)
        {
             //Raise Event to handle Condition
             OnSomeCondition();
        }
        Sleep(100);
    }
}

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

Теперь я задаюсь вопросом, есть ли лучший способ выполнить такую ​​работу, как функция windows, чтобы подключиться к ней, которая может запускать мои методы всего за x сек. Или мне нужно закодировать глобальное событие для моего приложения, подняв все x secs и позвольте ему вызвать мои методы следующим образом:

//Event from Windows or selfmade
TicEvent += new TicEventHandler(WatchForSomething));

а затем этот метод:

    void WatchForSomething()
    {
        if(SomeCondition)
        {
             //Raise Event to handle Condition
             OnSomeCondition();
        }
    }

Итак, я надеюсь, что это не закрыто из-за "субъективного вопроса" или чего-то еще, я просто хочу знать, какая наилучшая практика для такого рода работ.

4b9b3361

Ответ 1

Не обязательно "лучший способ" написать длинный код обработки событий.. Это зависит от того, какое приложение вы разрабатываете.

Первый пример, который вы показываете, - это идиоматический способ, с помощью которого вы часто видите основной метод написания длинного потока.. Хотя обычно желательно использовать примитив синхронизации мьютекса или ожидающего события, чем вызов Sleep() - в противном случае это типичный шаблон, используемый для реализации циклов обработки событий. Преимущество этого подхода заключается в том, что он позволяет специализированной обработке работать в отдельном потоке, что позволяет основному потоку приложения выполнять другие задачи или реагировать на ввод пользователя. Недостатком этого подхода является то, что для обеспечения возможности совместного использования разделяемых ресурсов может потребоваться использование барьеров памяти (например, блокировок). Это также затрудняет обновление вашего пользовательского интерфейса, поскольку вы должны, как правило, маршировать такие вызовы обратно в поток пользовательского интерфейса.

Также часто используется второй подход - особенно в системах, в которых уже есть API-интерфейс событийного типа, такой как WinForms, WPF или Silverlight. Использование объекта таймера или события Idle является типичным способом в котором могут выполняться периодические проверки фона, если нет инициированного пользователем события, которое запускает вашу обработку. Преимущество здесь в том, что он легко взаимодействует и обновляет объекты пользовательского интерфейса (поскольку они напрямую доступны из одного потока), и это уменьшает необходимость блокировок и мьютексов для защищенных данных. Одним из потенциальных недостатков этого подхода является то, что обработка, которая должна выполняться, занимает много времени, она может сделать ваше приложение невосприимчивым к пользовательскому вводу.

Если вы не пишете приложения, имеющие пользовательский интерфейс (например, службы), то первая форма используется гораздо чаще.

Как вариант... если возможно, лучше использовать объект синхронизации, например EventWaitHandle или Semaphore, чтобы сигнализировать, когда работа доступна для обработки. Это позволяет избежать использования объектов Thread.Sleep и/или Timer. Это уменьшает среднюю задержку между тем, когда доступна работа, и когда запускается код обработки событий, и минимизирует накладные расходы на использование фоновых потоков, поскольку они могут быть более эффективно запланированы средой выполнения и не будут потреблять какие-либо циклы процессора пока там не будет сделано.

Также стоит упомянуть, что если обработка выполняется в ответ на связь с внешними источниками (MessageQueues, HTTP, TCP и т.д.), вы можете использовать такие технологии, как WCF для обеспечения скелета вашего кода обработки событий. WCF предоставляет базовые классы, которые значительно упрощают внедрение как клиентских, так и серверных систем, которые асинхронно реагируют на активность событий связи.

Ответ 2

Кстати, Thread.Sleep, вероятно, никогда не будет хорошей идеей.

Основная проблема с Thread.Sleep, о которой люди обычно не знают, заключается в том, что внутренняя реализация Thread.Sleep не передает сообщения STA. Лучшая и простая альтернатива, если вам нужно подождать определенное время и не может использовать объект синхронизации ядра, заключается в замене Thread.Sleep на Thread.Join на текущий поток с требуемым таймаутом. Thread.Join будет вести себя одинаково, т.е. поток будет ждать требуемое время, но тем временем объекты STA будут накачаны.

Почему это важно (следует некоторое подробное объяснение)?

Иногда, даже если вы даже не знаете, один из ваших потоков мог создать объект STA COM. (Например, это иногда происходит за кулисами, когда вы используете Shell API). Теперь предположим, что ваш поток создал объект STA COM и теперь находится в вызове Thread.Sleep. Если когда-нибудь COM-объект должен быть удален (что может произойти в непредвиденное время GC), тогда поток Finalizer попытается вызвать объект distruvtor. Этот вызов будет привязан к потоку STA объекта, который будет заблокирован.

Теперь на самом деле у вас будет заблокированный поток Finalizer. В таких ситуациях объекты не могут быть освобождены из памяти, а за ними последуют плохие вещи.

Итак, нижняя строка: Thread.Sleep= bad. Thread.Join= разумная альтернатива.

Ответ 3

Если вы посмотрите Reactive Extensions, он предлагает элегантный способ сделать это с помощью наблюдаемый рисунок.

var timer = Observable.Interval(Timespan.FromMilliseconds(100));
timer.Subscribe(tick => OnSomeCondition());

Хорошая вещь о наблюдаемых - это способность составлять и комбинировать дальнейшие наблюдаемые от существующих, и даже использовать выражения LINQ для создания новых. Например, если вы хотите иметь второй таймер, который был синхронизирован с первым, но только триггер каждые 1 секунду, вы могли бы сказать

var seconds = from tick in timer where tick % 10 == 0 select tick;
seconds.Subscribe(tick => OnSomeOtherCondition());

Ответ 4

Первый пример, который вы показываете, является довольно неэлегантным способом реализации периодического таймера. У .NET есть несколько объектов таймера, которые делают подобные вещи почти тривиальными. Посмотрите System.Windows.Forms.Timer, System.Timers.Timer и System.Threading.Timer.

Например, здесь вы можете использовать System.Threading.Timer для замены первого примера:

System.Threading.Timer MyTimer = new System.Threading.Timer(CheckCondition, null, 100, 100);

void CheckCondition(object state)
{
    if (SomeCondition())
    {
        OnSomeCondition();
    }
}

Этот код будет вызывать CheckCondition каждые 100 миллисекунд (или около того).

Ответ 5

Вы не предоставляете много информации о том, почему вы это делаете, или о том, что вы пытаетесь выполнить, но если это возможно, вам может понадобиться создать службу Windows.

Ответ 6

Используйте BackgroundWoker для дополнительных мер безопасности потока:

BackgroundWorker bw = new BackgroundWorker();


bw.WorkerSupportsCancellation = true;


bw.WorkerReportsProgress = true;
.
.
.
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;

    for (;;)
    {
        if (worker.CancellationPending == true)
        {
           e.Cancel = true;

           break;
        }

        else
        {
            // Perform a time consuming operation and report progress.

            System.Threading.Thread.Sleep(100);
        }
    }
}

Для получения дополнительной информации посетите: http://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx

Ответ 7

Очень простой способ для неблокирования ждать других потоков/задач:

(new ManualResetEvent(false)).WaitOne(500); //Waits 500ms