3

I'm trying to run two methods in parallel. The first method connects to an ftp server and downloads some data. Because I want to reduce the network traffic it should run every 30 s. In parallel I want another method to run independent from the first method every 10 s.

The problem is I don't get the methods running/delayed in parallel.

namespace Example
{
    class Program
    {
        static async Task Main(string[] args)
        {
            await Task.Run(async () =>
            {
                while (true)
                {
                    await Every10s();
                    await Every30s();
                }
            });
        }

        public static async Task<bool> Every10s()
        {
            await Task.Delay(10000);
            Console.Writeline("10s");
            return true;
        }

        public static async Task<bool> Every30s()
        {
            await Task.Delay(30000);            
            Console.Writeline("30s");
            return true;
        }
    }
}

I would expect the following output with the corresponding pauses in between: 10s 10s 10s 30s 10s 10s 10s 30s ...

But instead both methods wait for each other so I get the output 10s 30s 10s 30s 10s 30s with a 40s pause.

Any help and hints are appreciated.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
  • 2
    Instead of awaiting the Tasks the method return, you should store them in a List and perform `await Task.WhenAll(list)` on it. You'd need to do that on each while-cycle. Note, for this use case I'd suggest using timers over async/await since this is literally the definiton of a timer. Another reason not to use async/await here is because async/await isn't truly parallel (I'd link to an awesome write up here but I forgot where it's from). – Joelius Jun 24 '19 at 12:09
  • You are mixing up parallel execution and asynchronous execution. Async isn't parallel. – iSpain17 Jun 24 '19 at 12:10
  • @iSpain17 very true, forgot to mention that. I'll add it. – Joelius Jun 24 '19 at 12:11
  • 2
    How is this program going to end? There is no way of exiting the infinite loop! – Theodor Zoulias Jun 24 '19 at 12:16
  • I forgot where the write up is from but there are so many sources on this like [this answer](https://stackoverflow.com/a/14099602/10883465), [this question](https://stackoverflow.com/questions/35126393/async-await-and-parallel) and many more if you search a bit. – Joelius Jun 24 '19 at 12:16
  • @Joelius, thanks for pointing that out, I'll also give timers a try. – Pietr Podschinski Jun 24 '19 at 12:42

1 Answers1

4

Because I want to reduce the network traffic it should run every 30 s. In parallel I want another method to run independent from the first method every 10 s.

You have two independent loops of work, so you need two loops in your code:

async Task RunEvery10s()
{
  while (true)
    await Every10s();
}

async Task RunEvery30s()
{
  while (true)
    await Every30s();
}

await Task.WhenAll(RunEvery10s(), RunEvery30s());
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • 1
    When and how will this program terminate? – Joelius Jun 24 '19 at 12:16
  • 1
    It will exit when all foreground threads have exited, just like any other program. – Stephen Cleary Jun 24 '19 at 12:19
  • But the call to `Task.WhenAll` won't ever return right? None of the passed in tasks will ever complete so the `Task.WhenAll` call won't return either or am I missing something? – Joelius Jun 24 '19 at 12:23
  • The `WhenAll` is a way to observe exceptions. If either of the operations fail, their exception will end up being thrown from `await Task.WhenAll`. – Stephen Cleary Jun 24 '19 at 12:26
  • Fair enough but this assumes that both methods implement some sort of cancellation by using an exception. I guess I'm missing some sort of handling the cancellation on the callers end, that's why I was irritated. – Joelius Jun 24 '19 at 12:31
  • I was referring to any kind of exception. If no exception is raised, these loops won't prevent the process from exiting. I agree cancellation would be a good thing to add, but as it wasn't in the op's original code, I didn't add it here, either. – Stephen Cleary Jun 24 '19 at 12:33
  • 1
    Thank you! I tried that before but without await methodname(); Works fine. I already implemented cancellation exceptions; the code was just a simplified example. – Pietr Podschinski Jun 24 '19 at 12:40
  • *"If either of the operations fail, their exception will end up being thrown from `await Task.WhenAll`"* -- I think that a failure of one operation alone will not get propagated by the `await Task.WhenAll`. Both operations must complete one way or another, for the `await Task.WhenAll` to complete. – Theodor Zoulias Mar 30 '22 at 01:40