【问题标题】:How to manage exception in Parallel.Invoke?如何在 Parallel.Invoke 中管理异常?
【发布时间】:2018-05-24 08:20:45
【问题描述】:

我有以下情况:

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

如您所见,有一个迭代集合的 foreach,对于每个调用,我需要等待 DoWork 完成执行,然后 foreach 可以继续。

问题是在DoWork 内部我正在使用Parallel 并且我得到一个“冻结”程序的异常,特别是没有显示任何异常,这是我的实现:

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

我在Foo方法里面加了一个Console.WriteLine比如:

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

Foo2 方法内部生成了一个异常,但我看不到是哪个异常,因为我在catch 上设置了一个断点,而这并没有触发。

我做错了什么?

【问题讨论】:

  • 这不是问题所在,而是您的 DoWork() 方法不会异步运行。无论如何,当我尝试您的代码的可编译版本时,我会看到异常。你能发布一个可编译的复制品吗?
  • 我想你的问题实际上是DoWork().Wait,你对异步代码的阻塞。不要这样做。把它放在一个线程上或一路异步
  • @ClayVerValen 因为正如我在问题中所写,我添加了 Console.WriteLine 以了解哪个方法被调用以及哪个失败,第三种方法没有被调用,因此前两种方法之一会产生异常
  • 未调用 Foo3 的事实并不意味着正在生成异常。这只意味着它没有被调用。如果您的程序冻结,我怀疑 Foo1 正在做一些由于 Foo2 正在做的事情而无法继续执行的操作,反之亦然。如果您的程序在调用 Foo3 之前退出,那么可能存在异常,但您只提到了冻结。

标签: c# multithreading


【解决方案1】:
  1. 您的 DoWork() 方法不会异步运行,因为它缺少任何调用 到await
  2. 当我尝试使用您的代码的可编译版本时,我确实看到了异常。

无论如何,如果您想让DoWork() 正确异步,您需要使用Task.Run()await Task.WhenAll(),如下所示:

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()");
        }
    }
}

如果您编译并运行该代码,您将看到以下消息:

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.

请记住,此答案仅适用于 C# 7.1 或更高版本 Async Main

【讨论】:

  • 感谢您的回答,也许我理解了问题,在await Task.WhenAll 之前我有:var result = await Task.Run(() =&gt; SomeMethod()); 之后代码到达此行应用程序崩溃,不应该使用等待吗?
  • @Charanoglu 当您使用await 时,等待任务中发生的任何异常都将在调用await 的线程中重新抛出(这是设计使然,以便您可以捕获并在适当的线程中处理异常,即通常是 UI 线程)。
  • @Charanoglu 阅读一下,它会比我更好地解释其中一些事情! msdn.microsoft.com/en-us/magazine/jj991977.aspx
  • 另外我应该指出,您可以将 try/catch 从 DoWork() 移到 awaitMain() 周围,它仍然可以工作(这更有可能是你想在典型代码中处理它的地方)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-26
  • 2016-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多