1

I have to test a boolean every 250ms, max 2 seconds, and stop when it's true.
Tried this, but Thread.Sleep isn't a good solution for me because it's freezing my software, and waiting for even more won't change anything because the boolean won't change while it's frozen.

bool onLine = false;
Stopwatch chrono = new Stopwatch();
chrono.Start();
while (chrono.ElapsedMilliseconds < 2000)
{
    if (!State().Equals(Online))
    {
        System.Threading.Thread.Sleep(250);
    }
    else
    {
        onLine = true;
        operationCompleted();
        break;
    }
}
if (!onLine)
{
    MessageBox.Show("Error");
}

I don't know how to make the tests in an event raised by a timer if someone has a correct implementation?

Yannick Meeus
  • 5,643
  • 1
  • 35
  • 34
betsou
  • 143
  • 3
  • 16
  • 3
    If your idea is a timer, why didn't you try a timer? – Thomas Weller Jun 07 '17 at 14:47
  • Cant you put your logic into the setter of the boolean ? – Rand Random Jun 07 '17 at 14:48
  • I searched a little with timers but I don't found the correct implementation to do what I need – betsou Jun 07 '17 at 14:49
  • Possible duplicate of [What is the best way to implement a "timer"?](https://stackoverflow.com/questions/12535722/what-is-the-best-way-to-implement-a-timer) – Thomas Weller Jun 07 '17 at 14:50
  • Instead of repeatedly checking the state of a Boolean variable, you could instead try what @RandRandom suggests and add logic to the property setter, and/or you could set up an event handler for when the item you're watching changes. Either way would be better than the constant polling you're trying to do with this approach. – Knowledge Cube Jun 07 '17 at 14:51
  • There are different timers, depending on whether you have a Console application, Windows Forms application etc. It seems you have some UI. Please read the linked posts and come back when you tried some of the given implementations. – Thomas Weller Jun 07 '17 at 14:51
  • 1
    "Use a Timer" can be implemented more succinctly nowadays with an `async` loop and `await Task.Delay`. Makes for a much easier read than Timers and their callbacks. – spender Jun 07 '17 at 14:52
  • The problem with timers is that it raise an event, and I can't put the tests in the event, or the event will have to call the Timer too if the test is negative ? I can't put logic in the boolean, it's a method of an unmanaged C++ DLL that I call to have the Device State. And I can't add an event in the DLL. – betsou Jun 07 '17 at 14:57
  • And It's a WPF application – betsou Jun 07 '17 at 15:00
  • @spender Do you have sample code of this async loop ? – betsou Jun 07 '17 at 15:01

5 Answers5

5

The currently accepted answer is not a particularly good use of the async await paradigm, and introduces a lot of noise for something that need not be complicated. Simply scheduling synchronous, blocking code onto the ThreadPool with Task.Run rather than upgrading to fully async code is a half-measure.

Rewriting that code without the blocking code and the spurious Task.Run, it could be as simple as:

private async void button1_Click(object sender, EventArgs e)
{
    bool onLine = false;
    Stopwatch chrono = new Stopwatch();
    chrono.Start();
    while (chrono.ElapsedMilliseconds < 2000)
    {
        if (!State().Equals(Online))
        {
            await Task.Delay(250);
        }
        else
        {
            onLine = true;
            break;
        }
    }

    if (onLine)
    {
        operationCompleted();
    }
    else
    {
        MessageBox.Show("Error");
    }
}
spender
  • 117,338
  • 33
  • 229
  • 351
1

Not sure what it looks like in WPF, but here is how it would be done in WinForms. Note that the button handler has been marked with async:

private async void button1_Click(object sender, EventArgs e)
{
    bool onLine = false;
    Stopwatch chrono = new Stopwatch();
    await (Task.Run(new Action(() =>
    {          
        chrono.Start();
        while (chrono.ElapsedMilliseconds < 2000)
        {
            if (!State().Equals(Online))
            {
                System.Threading.Thread.Sleep(250);
            }
            else
            {
                onLine = true;
                break;
            }
        }
    })));

    if (onLine)
    {
        operationCompleted();
    }
    else
    {
        MessageBox.Show("Error");
    }
}
Idle_Mind
  • 38,363
  • 3
  • 29
  • 40
  • Thank you ! Why do you mark the handler with 'async' ? I don't want this handler to be called many times at the same times ! – betsou Jun 07 '17 at 15:10
  • `async` and `await` **work together** allowing you to write a "synchronous block" of code in a UI handler while pushing the work to another thread making it asynchronous/threaded. See [here](https://msdn.microsoft.com/library/hh191443(vs.110).aspx) and [here](https://msdn.microsoft.com/en-us/magazine/mt620013.aspx) for a good place to start reading about the concepts. – Idle_Mind Jun 07 '17 at 15:35
  • This is a poor choice of "accepted answer", but could be improved. I'm not happy about the mixing of `Thread.Sleep`, `Task.Run` and `async/await` in a method that is already marked `async`. All this is doing is shunting a synchronous, blocking sleep onto another thread and awaiting its completion. Running this sort of blocking code in the threadpool isn't the best of ideas. It's not necessary to block, nor to use Task.Run. I've written [an answer below](https://stackoverflow.com/a/44419915/14357) that directly addresses the problems with this answer. – spender Jun 07 '17 at 18:10
1

Here's an example using a threading timer:

public class Foo
{
    public System.Threading.Timer EventTimer
    {
        get;
        set;
    }

    public long TimingInterval
    {
        get;
        set;
    }
    public Foo()
    {
        EventTimer = new System.Threading.Timer(new TimerCallback(Foo.DoCheck),
            this,
            this.TimingInterval,
            this.TimingInterval);
    }
    public void DoCheck()
    {
        //do check here
    }
MORCHARD
  • 263
  • 2
  • 12
1

Here's one way this could be accomplished with a timer. The idea is to set the interval to how often you want to check, and set a timeout variable to the time you want to stop checking. Then, in the Tick event (which triggers every interval), do your online check, followed by a check of the timeout.

[Updated for WPF to use a DispatcherTimer]

System.Windows.Threading.DispatcherTimer timer = new DispatcherTimer(); 
private DateTime timeout;

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    timeout = DateTime.Now.AddSeconds(2);
    timer.Tick += Timer_Tick;
    timer.Interval = TimeSpan.FromMilliseconds(250);
    timer.Start();
}

private void Timer_Tick(object sender, EventArgs e)
{
    if (State().Equals(Online))
    {
        onLine = true;
        operationCompleted();
        timer.Stop();
    }

    if (DateTime.Now > timeout)
    {
        timer.Stop();
        MessageBox.Show("Error");
    }
}

// rest of code omitted
Rufus L
  • 36,127
  • 5
  • 30
  • 43
0

You can use a BackgroundWorker.

BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;

The DoWork event handler of a BackgroundWorker is executed on a deditecated thread, so your software will not freeze.

private void bw_DoWork(object sender, DoWorkEventArgs e)
{
    bool onLine = false;

    Stopwatch chrono = new Stopwatch();
    chrono.Start();

    while (chrono.ElapsedMilliseconds < 2000)
    {
        if (!State().Equals(Online))
        {
            System.Threading.Thread.Sleep(250);
        }
        else
        {
            onLine = true;
            break;
        }
    }

    e.Result = onLine;
}

When the background operation has completed, the RunWorkerCompleted event occurs. You can pass an object from DoWork to RunWorkerCompleted by using e.Result.

private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    bool onLine = (bool)e.Result;
    if (onLine)
    {
        operationCompleted();
    }
    else
    {
        MessageBox.Show("Error");
    }
}

enter image description here

DoWork occurs when RunWorkerAsync is called. You have to call bw.RunWorkerAsync(); to start your BackgroundWorker.

Omar Muscatello
  • 1,256
  • 14
  • 25