Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait task for x amount of time and report progress

I need a better design for sleeping Task than Thread.Sleep, I'm using the Task class.

In my wpf app i run a Task, this task runs another couple of tasks, each of this tasks first login to and Internet website, after they login they need to wait couple of seconds and inform the user when they will proceed further and this is my problem, because with Thread.Sleep i can't report the progress.

There are more functions than login, it's about 5-6, all of them request an Internet resource, and between them there need to be a sleep time with remaining time report send do gui, this 5-6 function are all in one Task, but there could be many tasks.

What i need is to make the task wait, but also allow it to send updates on the remaining time to proceed to the GUI.

Do you have any ideas for this problem, how to make it better, maybe you have some design patterns already for such problem?

I have heard also that using Thread.Sleep is a poor design practice.

EDIT: No one knows ? Whats with some kind of self contained timer with thread wait like waithandle, autoresetevent, anybody?

like image 750
Programista Avatar asked Nov 06 '11 19:11

Programista


3 Answers

The solution is to treat it as async callback, rather than a synchronous wait. If you are on the Async CTP, the right way would be to:

async item => { await Task.Delay(1000); Process(item); }

This also seems like an ideal use case for DataFlow or Rx.

Using Reactive Extensions:

static void Track(int timeout, int frequency, string item)
    {
        Observable.Interval(TimeSpan.FromSeconds(frequency)) //produces 0, 1, 2.. with the interval
                  .Do(i => Console.WriteLine("Working on {0}", item)) // work on item
                  .TakeUntil(Observable.Timer(TimeSpan.FromSeconds(timeout))) //stop once the timer publishes a value
                  .Subscribe
                  (
                        i => Console.WriteLine("Reporting {0}%", ((double)(i + 1) / timeout * 100)), // the interval reaches OnNext
                        e => Console.WriteLine("Error!"), // error occured 
                        () => Console.WriteLine("Completed") // observable completed
                  );
    }

On calling this with Track(timeout: 5, frequency: 1, item: "http://example.com/?"), the output produced is:

Working on http://example.com/?
Reporting 20%
Working on http://example.com/?
Reporting 40%
Working on http://example.com/?
Reporting 60%
Working on http://example.com/?
Reporting 80%
Completed
like image 113
Asti Avatar answered Nov 06 '22 06:11

Asti


Forget using Thread.Sleep. Instead, run your task in a background thread and use a WaitHandle with an AutoResetEvent. (Links: WaitHandle/WaitOne / AutoReset)

Your background thread can send updates to UI with async delegates, but the calling thread will wait it until either thing happens:

1) Your thread reports it has completed its task by using yourwaithandle.Set();

or

2) the waiting thread timeouts (timeout value is set as parameter to WaitOne() -method).

like image 26
Lauri I Avatar answered Nov 06 '22 08:11

Lauri I


Rather than having a single task perform all of those half-dozen functions (login, etc.), make each function a separate task. You can then create a priority queue, ordered by time. When you want to start one of these meta-tasks, you place its first function into a queue along with the state data (i.e. url, user id, password, etc.).

As each function is completed, it queues the next function to be performed. So if the first two functions are login and get_data, you'd have:

queue login with state data to execute at DateTime.Now
When login is finished, it queues get_data with the state data and DateTime.Now + 5 seconds (or whatever time)

When the last function executes, it posts the results somewhere.

You can then set up a timer that will poll the queue 10 times per second (or have the timer's next tick time be updated whenever something is added to the queue). It can start individual tasks as required.

The new Async CTP might have this kind of thing already: a "execute task at time". Might be worth looking into it.

As for reporting progress, each function can report progress when it starts (i.e. "logging in" ... "waiting 5 seconds to get data" ... "getting data," etc.) If you want, you can make the timer thread walk the priority queue periodically and report when particular tasks will be executed. Although that might be overkill.

And what you heard is right: Thread.Sleep is an exceptionally bad idea, especially when you're working with thread pool threads.

like image 26
Jim Mischel Avatar answered Nov 06 '22 07:11

Jim Mischel