1

I have a simple application that connects to API, scans a file and checks its status. Scanning can take a period of time, and I want to 'ask' api every 5 seconds about the file status, and, if it's clean - return true, if other - return false. The problem is that I don't know how to tell the program to wait until the condition have been met before returning value. My code looks something like this:

bool Scan(string filePath){
    Scanner.Scan(filePath);
    //now repeat line below every 2s until the result is positive
    var result = Scanner.CheckStatus();
    // I dont want to go below before status is positive or x seconds has elapsed
    return result
}
Kuker
  • 65
  • 3
  • 11
  • Well the code you have will run Scanner.Scan and on completion run result=scanner.checkstatus, so, you need to look into either threads or tasks to move the call for the scan out, and then montior the process – BugFinder Nov 28 '16 at 11:36
  • I tried using timer, and it ran CheckStatus every 2 s but this code here still went on and returned result before CheckStatus could finish – Kuker Nov 28 '16 at 11:38
  • Is there a `StatusChanged` event in your `Scanner`? If not you could implement it. Then you could use an [`AutoResetEvent`](http://stackoverflow.com/a/2820911/284240). – Tim Schmelter Nov 28 '16 at 11:38

3 Answers3

3

Threads

Below example will put down to sleep Your thread. This means You are unable to do anything else during that time, if Your application is single threated.

Code that sleep the thread is:

System.Threading.Thread.Sleep(1000); //time in ms, 1 sec

Comment by @EpicSam: This will make your program unresponsive during the sleep. In case you have some sort of user interface that needs to be responsive, consider using line below instead:

await Task.Delay(5000);

In example, it should looks like:

bool Scan(string filePath){
    Scanner.Scan(filePath);
    var result = Scanner.CheckStatus();
    while(result != ResultType.Positive)
    {
        System.Threading.Thread.Sleep(5000); //time in ms
        result = Scanner.CheckStatus();
    }
    return result
}

Simplified:

bool Scan(string filePath){
    Scanner.Scan(filePath);
    ResultType result;

    while((result = Scanner.CheckStatus()) != ResultType.Positive)
        System.Threading.Thread.Sleep(5000); //time in ms

    return result
}

Timers & events

Another way to handle the case is use Timers and Evenets. You will set up timer, it will do the work each time You want. Once it find the result is done, it will send message to You. This is the correct way, however putting Thread to Sleep also works.

Check Comparing Timer with DispatcherTimer to choose which Timer to use.

  • Please note that the code below make different results then for Threads -> it does run the whole Scan method in loop. Which includes running the Scanner.Scan() method

-

using System.Timers;

public class MyClass
{
    Timer mTimer;
    string mFilePath;

    public delegate void StatusCheck(bool value);
    public event StatusCheck StatusChecked; 

    MyClass(string filePath)
    {
        mFilePath = filePath;

        mTimer = new Timer(); //time in ms
        mTimer.Elapsed += new ElapsedEventHandler(Scan);
    }

    public void Start()
    { mTimer.Enabled=true; }

    private void Scan(object source, ElapsedEventArgs e)    
    {
        Scanner.Scan(filePath);
        ResultType result;
        if(result = Scanner.CheckStatus())
        {
            this.StatusChecked?.Invoke(result); 
        }
    }

}

Usage will look like:

//some code ...
MyClass checker = new MyClass("C:\a.txt");
checker.StatusChecked += new StatusCheck(myVoid);
checker.Start();

//some more code ..

private void myVoid(bool result)
{
    Console.WriteLine("My result is: " + result );
}
Community
  • 1
  • 1
Tatranskymedved
  • 4,194
  • 3
  • 21
  • 47
  • Great answer, I'm going to try to implement it. – Kuker Nov 28 '16 at 11:42
  • This will make your program unresponsive during the sleep. In case you have some sort of user interface that needs to be responsive, consider using `await Task.Delay(5000)` instead. – EpicSam Nov 28 '16 at 11:57
  • `await Task.Delay` will be much cleaner solution even without UI. – Fabio Nov 28 '16 at 12:03
3

async/await approach looks clear enough. You can use async/await even you didn't have UI.

async Task<bool> Scan(string filePath)
{
    Scanner.Scan(filePath);
    var result = Scanner.CheckStatus();

    while(result == false)
    {
        await Task.Delay(2000);
        result = Scanner.CheckStatus();
    }

    return result
}

Because your Scanner.Scan method working with IO - it become very good candidate for asynchronous method which uses async/await.

Fabio
  • 31,528
  • 4
  • 33
  • 72
  • It looks very clean! I'm going to try it too. Thanks! – Kuker Nov 28 '16 at 12:14
  • Ok, I am accepting this answer, it works like a charm and I don't have to use any timers. Async/Await rocks! Edit: just for the note, i had to use "await" on Scanner.CheckStatus() (It's async), cause without await it hanged. – Kuker Nov 28 '16 at 12:23
  • @Kuker, if `Scanner.CheckStatus` is asynchronous method the I suggest to rename it by adding suffix "Async". – Fabio Nov 28 '16 at 13:11
0
bool Scan(string filePath)
{
    var timeout = new TimeSpan(0,0,5);
    Scanner.Scan(filePath);
    Stopwatch stopwatch = Stopwatch.StartNew();

    do 
    {
        result = Scanner.CheckStatus();
        if(result) return true;

        System.Threading.Thread.Sleep(2000);

    } while(stopwatch.Elapsed < timeout)

    return false;
}

note it is a simple but naive implementation as it does not prevent the CheckStatus() call itself to exceed the 5 seconds timeout.

LeBaptiste
  • 1,156
  • 14
  • 20