R.Gregory
R.Gregory

Reputation: 131

Triggering an event every second

I have an application that needs to check a website feed every second.

Sometimes the request to the server is longer than a second. In this case, the application needs to wait until the first request completes, then start a new request immediately. How could I implement this?

Also, the request should not freeze the GUI.

Upvotes: 10

Views: 5981

Answers (7)

M'Adil Memon
M'Adil Memon

Reputation: 9

use a timer like this:

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

Upvotes: 0

Enigmativity
Enigmativity

Reputation: 117010

I think that easiest way to deal with this now is to use the Microsoft Reactive Framework (Rx).

So, assuming I have a function that will call the website feed and return a Response object, like this:

Func<Response> checkWebsiteFeed = ...;

Then I can just do this to wire it all up:

var query =
    Observable
        .Interval(TimeSpan.FromSeconds(1.0))
        .Select(x => checkWebsiteFeed())            ;

query
    .ObserveOnDispatcher()
    .Subscribe(x =>
    {
        /* do something with response on UI thread */
    });

The checkWebsiteFeed is called on a background thread and the Subscribe is run on the UI thread. Super simple!

Upvotes: 0

G-Wiz
G-Wiz

Reputation: 7426

I would use a simple variation of the producer-consumer pattern. You'd have two theads, a producer and a consumer, that share an integer variable. The producer would have a System.Threading.Timer that fired every second, at which time it would Interlocked.Increment the variable and call the consumer. The consumer logic repeatedly checks the feed and Interlocked.Decrement the counter, while the counter is greater than zero. The consumer logic would be protected by a Monitor.TryEnter which would handle re-entrancy. Here's sample code.

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

Usage:

FeedCheck.Start();

A good resource on .NET Threading (besides the MSDN Library stuff) is Jon Skeet's documentation, which includes this example of producer-consumer under "More Monitor methods".

By the way, a true producer-consumer pattern revolves around a collection of work data, with one or more threads producing work by adding data to that collection, while one or more other threads consumer work by removing data from that collection. In our variation above, the "work data" is merely a count of the number of times we need to immediately check the feed.

(Another way to do it, instead of having the timer callback call Consume, is for the timer callback to lock and pulse a Monitor that Consume waits on. In that case, Consume has an infinite loop, like while(true), which you kick off one time in its own thread. Therefore there is no need to support re-entrancy with the call to Monitor.TryEnter.)

Upvotes: 9

maranas
maranas

Reputation: 1416

you could try creating a main application that would contain the GUI you wouldn't want to freeze. it would start a thread with a loop that would iterate after a specific amount of time. if the timer equals/exceeds set time, start another thread for the request. But, check if a thread already exists before starting. If not, then block execution until the request thread is done. e.g.:

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
}

Note: Don't do it every second. As other posters have said, it is a little disrespectful to the site owners. You risk getting denied access to the feed by the website owners.

Upvotes: 0

Ed Power
Ed Power

Reputation: 8531

Use a timer like this:

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

The elapsed event is handled on a ThreadPool thread, so you will have to keep this in mind if you need to update the UI from the elapsed handler.

Upvotes: 1

Ray Burns
Ray Burns

Reputation: 62909

I would be inclined to use a separate thread like this:

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

The Dispatcher.Invoke transitions to the UI thread for the actual UI update. This implements producer-consumer very efficiently.

The thread.IsBackground = true causes the thread to stop when your application ends. If you want it to stop sooner than that, set a "Stop" value to "true". The "Stop" value in the above code is assumed to be a bool property of the class, but it could be anything - even a local variable.

Upvotes: 4

Randy Minder
Randy Minder

Reputation: 48392

A Timer object might do the trick for you. You can set it to fire every second. The code that executes upon the Timer firing would first disable the timer, do it's work, and re-enable the timer. By disabling the timer, it won't fire again, until it's done processing.

Upvotes: 1

Related Questions