1

I'm new in C#, I've gone to it from Delphi. So may be I do something wrong. My app (windows service) make tasks to control on-off states and count "on" time. I've tried to use Task.Delay(x), but it seems I catch deadlocks... Main idea make tasks with infinite cycle which performs every x ms. I don't know if I could use the Timer for executing part code in lambda method of task...?

        int TagCnt = DataCtrl.TagList.Count;                     
        stopExec = false;
        if (TagCnt != 0)
        {                
            tasks = new Task[TagCnt];                
            for (int i = 0; i <= TagCnt - 1; i++)
            {                    
                int TempID = i;
                tasks[TempID] = Task.Run(async () => // make threads for parallel read-write tasks // async
                {                                            
                    Random rand = new Random();
                    TimeSpan delay = TimeSpan.FromMilliseconds(rand.Next(1000, 1500))                                              
                    try
                    {
                        while (!stopExec)
                        {                               
                        cToken.ThrowIfCancellationRequested();                          
                        //do basic job here
                        await Task.Delay(delay, cToken);
                        }//while end                            
                    }
                    catch (...)
                    {
                    ...
                    }                                            
                }, cToken);                   
            }
sad1002
  • 29
  • 4
  • 4
    If you want to do something every x ms, there is a component called [Timer](http://msdn.microsoft.com/en-US/library/system.threading.timer(v=vs.110).aspx). Recreating the wheel is a lot of work and most of the time it introduces more errors. – nvoigt Apr 02 '14 at 06:11

1 Answers1

2

A couple of things went wrong here:

  • The tasks you're creating are completed instantly, in your case Task.Factory.StartNew returns Task<Task> because of the async lambda. To make it work as expected, unwrap the inner task with Task.Unwrap. Also, remove TaskCreationOptions.LongRunning, you don't need it here:

tasks[TempID] = Task.Factory.StartNew(async () => { ...}).Unwrap();

tasks[TempID] = Task.Run(async () => { ...});
  • Besides, there's no synchronization context installed on a Windows service thread by default. Thus, the code after await Task.Delay() will be executing on a new pool thread each time, you should be ready for this.

  • Task.Delay is a bit different from a periodic timer. It will delay the execution. Use Stopwatch to calculate how much to delay for:

// beginning of the loop
stopwatch.Reset();
stopwatch.Start();

// the loop body
// ...

// end of the loop
await Task.Delay(Math.Max(500-Stopwatch.ElapsedMilliseconds, 0));
noseratio
  • 59,932
  • 34
  • 208
  • 486
  • Thank you, I've read the article. Interesting! But seems a little bit complex. I use stopwatch to calculate when "on" state and I use Delay(x) to make delay in infinite cycle for making measures every x ms. So I'll try test with Unwrap(). And I'm still searching how to execute inner part of my while by timer inside the same task... – sad1002 Apr 02 '14 at 07:53
  • If I will use OnTimerEvent where I'll place ContinueWith() where will stopwatch work. Will stopwatch continue work after end of ContinueWith? – sad1002 Apr 02 '14 at 07:55
  • @sad1002, *"how to execute inner part of my while by timer inside the same task..."* It **is** inside the same task. *Task* doesn't necessarily mean *thead*. You may switch threads inside the same task many times, or once, or never. – noseratio Apr 02 '14 at 07:56
  • @sad1002, you don't need `ContinueWith` for `OnTimeEvent`, you can `await` any event, same as you await the result of `Task.Delay`. Check [this](http://stackoverflow.com/q/22783741/1768303). – noseratio Apr 02 '14 at 07:58
  • This seems complex for me now. I understand idea, but I cannot make a realization with my code. How can call part of code which is situated in "while" section? When I place usual thread.sleep(x) all works fine, I get normal measurement, but CPU load about 10-12% – sad1002 Apr 02 '14 at 11:56
  • 1
    @sad1002, *"How can call part of code which is situated in "while" section?"* - not sure I understand the question. This code does get executed in a similar way as it does with `Thread.Sleep` instead of `Task.Delay`. The difference is with `Sleep` this thread would be blocked. With `Task.Delay`, in plain words, the current execution location is saved, the execution is suspended, and the thread goes back to the pool. After the delay, the execution is resumed on the thread where `Task.Delay` has completed. Resources to read: http://stackoverflow.com/tags/async-await/info – noseratio Apr 02 '14 at 12:05
  • I understand that this way it should be but result is different. With delay it seems that while executes one time until Delay, stops and never back. But under debugger I see it works... – sad1002 Apr 02 '14 at 13:05
  • @sad1002, I don't think I could help here any better. – noseratio Apr 02 '14 at 13:10
  • seems going to new task with 'code' (task.delay) and going back to 'code' (if (DevItem.Stat)) with previous values, so I get wrong result... I read that going deeper for new task with Task.Delay() will save the current context and put it back on coming back. Am I write? – sad1002 Apr 03 '14 at 04:45
  • As I mentioned in the answer, there's no synchronization context on a Windows Service thread by default, so there's nothing to be "saved" and "put back" by `await`. If you don't have good understanding of what synchronization context is and how it works, read the links I posted earlier. – noseratio Apr 03 '14 at 04:47
  • Also I saw that threads count in Task Manager differs with 'async-await', 'Task.Delay' and with 'Thread.Sleep'. In normal work situation with 'sleep' there are 30 threads, when async - about 18-20... – sad1002 Apr 03 '14 at 06:49
  • thank you. I update first message code. Now all work fine as it is. Future projects I will plan my code without infinite loop. – sad1002 Apr 03 '14 at 11:33
  • 1
    @sad1002, there's nothing wrong with infinite loop. It always can be cancelled using `CancellationTokenSource`. – noseratio Apr 03 '14 at 11:34
  • I''ll update code with 'code' await Task.Delay() 'code'. Seems working good. But when I ran it with 100 tasks, service began to eat cpu time up to 10% of power... – sad1002 Feb 19 '15 at 06:07
  • could you explain your loop example with Stopwatch? – sad1002 Feb 19 '15 at 06:18
  • @sad1002, I'm not sure what exactly to explain. I suggest you post a separate question instead of reviving a year old one. – noseratio Feb 19 '15 at 08:05