0

I have the following situation:

foreach(var item in collection)
{
    DoWork().Wait;
}

As you can see there is a foreach that iterate over a collection, for each call I need to wait that DoWork complete the execution, after that the foreach can continue.

The problem's that inside DoWork I'm using Parallel and I get an exception that "freeze" the program, in particular isn't displayed any exception, this is my implementation:

public async Task DoWork()
{
    Try
    {
        Parallel.Invoke(
               () => Foo(),
               () => Foo2(),
               () => Foo3());
    }
    catch(Exception e)
    {
       //No exception caught
    }
}

I added inside Foo methods a Console.WriteLine such as:

public void Foo()
{
    Console.WriteLine("foo 1");
}

inside the method Foo2 an exception is generated, but I cannot see which exception is, because I setted a breakpoint on catch and this isn't firing.

What I did wrong?

Charanoglu
  • 1,229
  • 2
  • 11
  • 30
  • 1
    Not that this is the problem, but your `DoWork()` method doesn't run asynchronously. Regardless, when I try a compilable version your code I see the exception. Can you post a compilable repro? – Matthew Watson May 24 '18 at 08:26
  • I'd imagine that your issue is actually `DoWork().Wait`, your blocking on async code. Don't do this. Put it on a thread or async all the way – Liam May 24 '18 at 08:29
  • @ClayVerValen because as I wrote in the question I added Console.WriteLine to understand which method is called and which fail, the third method isn't called so one of the first two methods generate an exception – Charanoglu May 24 '18 at 08:31
  • Possible duplicate of [Why does this async action hang?](https://stackoverflow.com/questions/14526377/why-does-this-async-action-hang) – Liam May 24 '18 at 08:31
  • @Liam But, I need to wait the end of `DoWork` before continue the iteration – Charanoglu May 24 '18 at 08:31
  • then use `await`. You either need to use `async`/`await` all the way or use a thread pool thread or don't use `async` at all – Liam May 24 '18 at 08:32
  • 1
    The fact that Foo3 is not called does not mean an exception is being generated. It only means that it did not get called. If your program freezes, I suspect that Foo1 is doing something that is blocked from continuing due to what Foo2 is doing _and_ vice-versa. If your program exits before calling Foo3 then perhaps there is an exception, but you only mention freezing. – Clay Ver Valen May 24 '18 at 08:35

1 Answers1

4
  1. Your DoWork() method does not run asynchronously because it lacks any calls to await.
  2. When I try a compilable version of your code, I do see the exception.

Regardless, if you want to make your DoWork() properly asynchronous, you'll need to use Task.Run() and await Task.WhenAll(), like so:

using System;
using System.Threading;
using System.Threading.Tasks;                                                    

namespace Demo
{
    class Program
    {
        public static async Task Main()
        {
            Console.WriteLine("Awaiting DoWork()");
            await DoWork();
            Console.WriteLine("Finished awaiting. Press <RETURN> to exit.");
            Console.ReadLine();
        }

        public static async Task DoWork()
        {
            try
            {
                await Task.WhenAll(
                    Task.Run(() => Foo()),
                    Task.Run(() => Foo2()),
                    Task.Run(() => Foo3())
                );
            }

            catch (Exception e)
            {
                Console.WriteLine("Caught exception: " + e.Message);
            }
        }

        public static void Foo()
        {
            Console.WriteLine("Starting Foo()");
            Thread.Sleep(1000);
            Console.WriteLine("Finishing Foo()");
        }

        public static void Foo2()
        {
            Console.WriteLine("Starting Foo2()");
            Thread.Sleep(500);
            Console.WriteLine("Foo2() is throwing an exception.");
            throw new InvalidOperationException("OOPS!");
        }

        public static void Foo3()
        {
            Console.WriteLine("Starting Foo3()");
            Thread.Sleep(250);
            Console.WriteLine("Finishing Foo3()");
        }
    }
}

If you compile and run that code, you'll see the following messages:

waiting DoWork()
Starting Foo()
Starting Foo2()
Starting Foo3()
Finishing Foo3()
Foo2() is throwing an exception.
Finishing Foo()
Caught exception: OOPS!
Finished awaiting. Press <RETURN> to exit.

Keep in mind this answer will only work with C# 7.1 or newer Async Main

Mike
  • 850
  • 10
  • 33
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • Thanks for the answer, maybe I understood the problem, before `await Task.WhenAll` I have: `var result = await Task.Run(() => SomeMethod());` after that the code reach this line the app crash, shouldn't use await? – Charanoglu May 24 '18 at 08:39
  • 1
    @Charanoglu When you use `await`, any exceptions that occured in the awaited task(s) will be rethrown in the thread that calls `await` (this is by design, so that you can catch and handle exceptions in the appropriate thread, i.e. normally the UI thread). – Matthew Watson May 24 '18 at 08:42
  • 1
    @Charanoglu Have a read of this, it will explain some of these things better than I can! https://msdn.microsoft.com/en-us/magazine/jj991977.aspx – Matthew Watson May 24 '18 at 08:43
  • Also I should point out that you can move the try/catch from `DoWork()` and put it around the `await` in `Main()` instead and it will still work (and that is more likely to be the place you'd want to handle it in typical code). – Matthew Watson May 24 '18 at 08:53