Инициирование события каждую секунду

У меня есть приложение, которое должно проверять канал веб-сайта каждую секунду.

Иногда запрос к серверу длиннее, чем секунда. В этом случае приложение должно ожидать, пока первый запрос не завершается, затем запустите новый запрос сразу. Как я мог реализовать это?

Кроме того, запрос не должен замораживать GUI.

9
задан G-Wiz 2 March 2010 в 16:21
поделиться

5 ответов

Я бы использовал простой вариант шаблона производитель-потребитель. У вас будет два theads, производитель и потребитель, которые совместно используют целочисленную переменную. У производителя должен быть System.Threading.Timer , который запускается каждую секунду, и в это время он Interlocked.Increment переменная и вызывает потребителя. Логика потребителя многократно проверяет подачу и Interlocked.Decrement счетчика, пока счетчик больше нуля. Логика потребителя будет защищена Monitor.TryEnter , который будет обрабатывать повторный вход. Вот пример кода.

public static FeedCheck{
  int _count = 0;
  static object _consumingSync = new object();
  static Threading.Timer _produceTimer;

  private static void Consume() {
    if (!Monitor.TryEnter(_consumingSync)) return;

    try {
      while(_count > 0) {
        // check feed
        Interlocked.Decrement(ref _count);
      }
    }
    finally { Monitor.Exit(_consumingSync); }
  }

  private static void Produce() {
    Interlocked.Increment(ref _count);
    Consume();
  }

  public static void Start() {
    // small risk of race condition here, but not necessarily
    // be bad if multiple Timers existed for a moment, since only
    // the last one will survive.
    if (_produceTimer == null) {
      _produceTimer = new Threading.Timer(
        _ => FeedCheck.Produce(), null, 0, 1000
      );
    }
  }
}

Использование:

FeedCheck.Start();

Хорошим ресурсом по .NET Threading (помимо материала библиотеки MSDN) является документация Джона Скита, которая включает этот пример производителя-потребителя в разделе «Подробнее Monitor ] методы ".

Между прочим, настоящий паттерн производитель-потребитель вращается вокруг коллекции рабочих данных, когда один или несколько потоков производят работу, добавляя данные в эту коллекцию, в то время как один или несколько других потоков-потребителей работают, удаляя данные из этой коллекции. В нашем варианте выше «рабочие данные» - это просто количество раз, которое нам нужно немедленно проверить фид.

(Другой способ сделать это, вместо использования обратного вызова таймера Consume, состоит в том, чтобы обратный вызов таймера блокировал и пульсировал Monitor , который ожидает Consume. В этом случае Consume имеет бесконечный цикл , например while (true) , который вы запускаете один раз в отдельном потоке. Поэтому нет необходимости поддерживать повторный вход с помощью вызова Monitor.TryEnter .)

8
ответ дан 4 December 2019 в 14:28
поделиться

Объект Timer может помочь вам. Вы можете настроить его на срабатывание каждую секунду. Код, который выполняется при срабатывании таймера, сначала отключит таймер, выполнит свою работу и снова включит таймер. Если отключить таймер, он не сработает снова, пока не завершит обработку.

1
ответ дан 4 December 2019 в 14:28
поделиться

вы можете попробовать создать основное приложение, которое будет содержать графический интерфейс, который вы бы не хотели замораживать. он запустит поток с цикла, который будет повторяться через определенное время. если таймер равен / превышает установленное время, запускает другой поток для запроса. Но перед запуском проверьте, существует ли уже поток. Если нет, то блокируйте выполнение до тех пор, пока поток запроса не будет завершен. например:

Main
{
    Create gui;
    Start loop thread;
}

Loop thread
{
    loop while not exit
    {
        timer();
        WaitForResource()
        start RequestThread
    }
}

RequestThread
{
    Lock resource/semaphore
    Request, Update GUI
    free semaphore
}

Примечание: Не делайте это каждую секунду. Как говорили другие плакаты, это немного неуважительно по отношению к владельцам сайтов. Вы рискуете получить отказ владельцев сайта в доступе к ленте.

0
ответ дан 4 December 2019 в 14:28
поделиться

Я бы предпочел использовать отдельный поток вроде этого:

var thread = new Thread(() =>
{
  while(!Stop)
  {
    var nextCheck = DateTime.Now.AddSeconds(1);

    CheckWebSite();
    Application.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
    {
      UpdateViewModelWithNewData();
    }));
    int millis = (int)(nextCheck - DateTime.Now).TotalMilliseconds();
    if(millis>0)
      Thread.Sleep(millis);
  }
});
thread.IsBackground = true;
thread.Start();

Dispatcher.Invoke переходит в поток пользовательского интерфейса для фактического обновления пользовательского интерфейса. Это очень эффективно реализует производитель-потребитель.

thread.IsBackground = true заставляет поток останавливаться, когда ваше приложение завершается. Если вы хотите, чтобы он остановился раньше, установите для параметра «Стоп» значение «истина». Предполагается, что значение Stop в приведенном выше коде является логическим свойством класса, но это может быть что угодно - даже локальная переменная.

4
ответ дан 4 December 2019 в 14:28
поделиться

Используйте такой таймер:

    System.Timers.Timer timer = new System.Timers.Timer(1000);

    public void StartTimer()
    {
        timer.Elapsed += new System.Timers.ElapsedEventHandler(this.TimerHandler);
        timer.Start();
    }

    private void TimerHandler(object sender, System.Timers.ElapsedEventArgs e)
    {
        DateTime start;
        TimeSpan elapsed = TimeSpan.MaxValue;

        timer.Stop();

        while (elapsed.TotalSeconds > 1.0)
        {
            start = DateTime.Now;

            // check your website here

            elapsed = DateTime.Now - start;
        }
        timer.Interval = 1000 - elapsed.TotalMilliseconds;
        timer.Start();
    }

Прошедшее событие обрабатывается в потоке ThreadPool, поэтому вы должны помнить об этом, если вам нужно обновить пользовательский интерфейс из обработчика прошедшего времени.

1
ответ дан 4 December 2019 в 14:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: