8

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?

Programista
  • 1,037
  • 5
  • 15
  • 38
  • 1
    I got a good answer to this question awhile back: http://stackoverflow.com/questions/7311468/best-practice-for-thread-delays-with-countdown-notification – drharris Nov 15 '11 at 18:16

4 Answers4

6

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
Asti
  • 12,447
  • 29
  • 38
  • Interesting, i can't use Async CTP because it is a production ready commercial application, but i will check that option the normal way with some helper classes. What do you thing of some auto or manual reset event in a self contained blocking scenario with internal timer count? – Programista Nov 09 '11 at 14:10
  • @Programista Blocking is not the solution. What you really need in this case is a callback. Like using Continuation Passing Style. I'll edit the answer to include a solution using Microsoft's Reactive Extensions (Rx) if you're interested. – Asti Nov 10 '11 at 05:38
  • I don't know Rx so that would help me. – Programista Nov 10 '11 at 09:21
  • @Programista I've added an example of using Rx to solve this. It's a different way of thinking about streams. – Asti Nov 10 '11 at 15:15
  • Unfortunately after meeting with co workers we decided to not use any non production ready library like async ctp and also not to use additional library for just one simple functionality (Rx). – Programista Nov 16 '11 at 09:58
1

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.

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
  • This is not a good solution, even in a multithreaded app you need a structural code, 5-6 function in each Task is good, they are all related, it would be a mess to make every function in separate task, every task is a quite chunk of code, i don't think queuing function is a real solution, i'm looking for something better, there is nothing wrong that these functions are together in one task, this code is good but the sleep part not. I refuse to think that there is no better way. It can't be "waiting 5 second" it must be a real countdown, and monitoring periodic is somewhat 'hack'. – Programista Nov 06 '11 at 22:09
  • @Programista: The point is that the only way to delay a thread is to sleep. And since sleep is bad, you have to come up with some other way to break things up. – Jim Mischel Nov 06 '11 at 22:12
  • That is not true, task can be stopped by other function but i need someone that has done it already to tell me how, especially the remaining time update. – Programista Nov 06 '11 at 22:14
1

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).

Lauri I
  • 228
  • 1
  • 4
  • 11
  • Yeah i was thinking the same thing from the beginning but no one was pointing in this direction, why i don't know also link form drharris is very helpful, so i will make some logic on my own, based on waithandle or reset event and timer. – Programista Nov 16 '11 at 10:01
0

Have you considered using a Timer (from System.Timers) to perform the interstitial "time remaining" message updates? Each of your tasks could set an expected time to completion value, then your timer task could be responsible for counting down and updating the UI as necessary. No Thread.Sleep required, and Timer will execute its Tick code on a thread pool thread.

Dave C
  • 429
  • 3
  • 9
  • Yes, but only as an internal reference in a scenario that something blocks thread and uses a timer as a tick count and then counts the ticks (tick could be 100ms, 1 second etc. based on approach) and send update to ui each second with remaining time, if the time goes to zero, thread is released and the self contained block thread, count & update ui is disposed. – Programista Nov 09 '11 at 14:06
  • I'm talking about a global overall timer that would monitor the individual tasks' progress. I think you're trying to do too much within the individual tasks; if the end of a task also disposes UI, that raises a red flag for me. – Dave C Nov 09 '11 at 14:22
  • No, no, it can't work as a monitor, the task itself should be blocked for example for 3 seconds but i need to display in the ui "wait 3..2..1" second and so on. Maybe one timer could be used as a global 100 ms time reference for countdown purposes for other objects, and the ui is not disposed, i meant the self contained individual object/class with logic that block thread, count remaining time and updated ui is disposed when time come to 0 and the thread is released. – Programista Nov 09 '11 at 14:34
  • A thread cannot be both blocked and updating the UI. You need to architect your software differently. Or perhaps something like this: – Dave C Nov 09 '11 at 14:42
  • oops... auto-submit :( Thread A is your work thread, and Thread B is your UI update thread. Thread A does work that blocks, while thread B is on a timer and updates the UI. Before every update of the UI, thread B checks for a signal from Thread A that its work is complete, and kills itself if thread A has completed. The bottom line, though, is that you will need at least two threads to accomplish what you're talking about. – Dave C Nov 09 '11 at 14:45
  • Using another thread was always the solution, it is obvious that if i block a thread it can update the ui and countdown, but i was looking more a good design pattern than what to use. As i was saying probably a self contained object with new thread, countdown and ui update. – Programista Nov 09 '11 at 21:17