C# catch未捕获多线程错误

C# catch未捕获多线程错误,c#,multithreading,task-parallel-library,C#,Multithreading,Task Parallel Library,下面是一个完整的控制台程序,它再现了我遇到的一个奇怪错误。程序读取包含远程文件URL的文件,每行一个。它启动了50个线程来下载它们 static void Main(string[] args) { try { string filePath = ConfigurationManager.AppSettings["filePath"], folder = ConfigurationManager.AppSettings["folder"];

下面是一个完整的控制台程序,它再现了我遇到的一个奇怪错误。程序读取包含远程文件URL的文件,每行一个。它启动了50个线程来下载它们

static void Main(string[] args)
{
    try
    {
        string filePath = ConfigurationManager.AppSettings["filePath"],
            folder = ConfigurationManager.AppSettings["folder"];
        Directory.CreateDirectory(folder);
        List<string> urls = File.ReadAllLines(filePath).Take(10000).ToList();

        int urlIX = -1;
        Task.WaitAll(Enumerable.Range(0, 50).Select(x => Task.Factory.StartNew(() =>
          {
              while (true)
              {
                  int curUrlIX = Interlocked.Increment(ref urlIX);
                  if (curUrlIX >= urls.Count)
                      break;
                  string url = urls[curUrlIX];
                  try
                  {
                      var req = (HttpWebRequest)WebRequest.Create(url);
                      using (var res = (HttpWebResponse)req.GetResponse())
                      using (var resStream = res.GetResponseStream())
                      using (var fileStream = File.Create(Path.Combine(folder, Guid.NewGuid() + url.Substring(url.LastIndexOf('.')))))
                          resStream.CopyTo(fileStream);
                  }
                  catch (Exception ex)
                  {
                      Console.WriteLine("Error downloading img: " + url + "\n" + ex);
                      continue;
                  }
              }
          })).ToArray());
    }
    catch
    {
        Console.WriteLine("Something bad happened.");
    }
}
我刚刚关闭了Lavasoft,现在WinDbg显示:

Critical error detected c0000374
(3c4.3494): Break instruction exception - code 80000003 (first chance)
ntdll!RtlReportCriticalFailure+0x4b:
00007fff`4acf1b2f cc              int     3
0:006> g
(3c4.3494): Unknown exception - code c0000374 (first chance)
(3c4.3494): Unknown exception - code c0000374 (!!! second chance !!!)
ntdll!RtlReportCriticalFailure+0x8c:
00007fff`4acf1b70 eb00            jmp     ntdll!RtlReportCriticalFailure+0x8e (00007fff`4acf1b72)
0:006> g
WARNING: Continuing a non-continuable exception
(3c4.3494): C++ EH exception - code e06d7363 (first chance)
HEAP[VIPJobsTest.exe]: HEAP: Free Heap block 0000007AB96CC5D0 modified at 0000007AB96CC748 after it was freed
(3c4.3494): Break instruction exception - code 80000003 (first chance)
ntdll!RtlpBreakPointHeap+0x1d:
00007fff`4acf3991 cc              int     3

您的异常不会抛出,因为您没有尝试获取它
WaitAll
方法基本上是一个
屏障
,它等待(哈哈)所有任务完成。它是
void
,因此您必须为您的任务保存一个参考,以便进行进一步的操作,如下所示:

var tasks = Enumerable.Range(0, 50).Select(x => Task.Factory.StartNew(() =>
{
    while (true)
    {
        // ..
        try
        {
            // ..
        }
        catch (Exception ex)
        {
            // ..
        }
    }
})).ToArray();

Task.WaitAl((tasks);

// investigate exceptions here
var faulted = tasks.Where(t => t.IsFaulted);
根据,当您使用静态或实例、方法或属性之一时,会传播异常。但是,这不是您的选项,因为您在这里使用的是
try/catch
。因此,您需要订阅活动:

为什么它不扔就跑

此应用程序域范围的事件提供了一种机制来防止触发异常升级策略(默认情况下,该策略会终止进程)

为了使开发人员更容易基于任务编写异步代码,
.NETFramework 4.5
更改了未观察到的异常的默认异常行为。尽管未观察到的异常仍会引发
未观察到的taskeexception
异常,默认情况下,进程不会终止。相反,无论事件处理程序是否观察到异常,都会在引发事件后由运行时处理异常。可以配置此行为。从
.NET Framework 4.5
开始,您可以使用configuration元素恢复到.NET Framework 4的行为并终止该过程:

<configuration> 
 <runtime> 
  <ThrowUnobservedTaskExceptions enabled="true"/> 
 </runtime> 
</configuration>

现在,回到你的代码。考虑使用静态<代码> HTTPcli客< /Cult>实例,而不是<代码> HttpWebRequest < /C>,因为您只需要一个结果字符串。该类设计用于多线程代码,因此其方法是线程安全的

另外,您应该为
StartNew
方法提供一个标志(,顺便说一句,但您仍然需要它):

指定任务将是一个长期运行的粗粒度操作,与细粒度系统相比,它涉及的组件更少、更大。它向投资者暗示,超额认购可能是有理由的

超额订阅允许您创建比可用硬件线程数更多的线程。它还向任务调度器提供提示,提示该任务可能需要额外的线程,以便它不会阻止本地线程池队列上其他线程或工作项的前进


您可以在任务上添加一个延续

 Task.Factory.StartNew(() => ...)
    .ContinueWith (task => 
    {
       If (task.isFaulted)
       {
           //task.Exception
           //handle the exception from this context
       }
    });

毕竟,问题出在Lavasoft Web Companion身上。尽管我已经禁用了它,但它仍然有一些东西在后台运行。卸载它,修复了这个问题。

您的示例中有一些代码实际上不受try/catch的保护。 那里的错误将在线程上引发未捕获的异常

这是一个重构(以前未受保护的代码的注释)。外部try无法捕获这些,因为它位于起始线程上。因此,它将在子线程上未处理

static void Main(string[] args)
{
    try
    {
        string filePath = ConfigurationManager.AppSettings["filePath"],
            folder = ConfigurationManager.AppSettings["folder"];
        Directory.CreateDirectory(folder);
        List<string> urls = File.ReadAllLines(filePath).Take(10000).ToList();

        int urlIX = -1;
        Task.WaitAll(Enumerable.Range(0, 50).Select(x => Task.Factory.StartNew(() =>
          {
              try
              {
                  while (true) // ** was unprotected
                  {
                      int curUrlIX = Interlocked.Increment(ref urlIX);  // ** was unprotected
                      if (curUrlIX >= urls.Count)   // ** was unprotected
                          break;                    // ** was unprotected
                      string url = urls[curUrlIX];  // ** was unprotected
                      try
                      {
                          var req = (HttpWebRequest)WebRequest.Create(url);
                          using (var res = (HttpWebResponse)req.GetResponse())
                          using (var resStream = res.GetResponseStream())
                          using (var fileStream = File.Create(Path.Combine                    (folder, Guid.NewGuid() + url.Substring(url.LastIndexOf('.')))))
                              resStream.CopyTo(fileStream);
                      }
                      catch (Exception ex)
                      {
                          Console.WriteLine("Error downloading img: " + url + "\n" + ex);
                          continue;
                      }
                  } // while
              } // try
          })).ToArray());
    }
    catch
    {
        Console.WriteLine("Something bad happened.");
    }
}
static void Main(字符串[]args)
{
尝试
{
字符串filePath=ConfigurationManager.AppSettings[“filePath”],
folder=ConfigurationManager.AppSettings[“文件夹”];
创建目录(文件夹);
List url=File.ReadAllLines(filePath).Take(10000).ToList();
int-urlIX=-1;
Task.WaitAll(可枚举的.Range(0,50)。选择(x=>Task.Factory.StartNew(()=>
{
尝试
{
而(true)/**未受保护
{
int curUrlIX=互锁。增量(参考urlIX);//**未受保护
如果(curUrlIX>=url.Count)/**未受保护
中断;//**未受保护
字符串url=url[curUrlIX];//**未受保护
尝试
{
var req=(HttpWebRequest)WebRequest.Create(url);
使用(var res=(HttpWebResponse)req.GetResponse())
使用(var resStream=res.GetResponseStream())
使用(var fileStream=File.Create(Path.Combine(文件夹,Guid.NewGuid()+url.Substring(url.LastIndexOf('.'))))
CopyTo(fileStream);
}
捕获(例外情况除外)
{
控制台.WriteLine(“下载img时出错:+url+”\n“+ex);
继续;
}
}//而
}//试试看
})).ToArray());
}
抓住
{
WriteLine(“发生了不好的事情”);
}
}

您的外部捕获不会捕获线程中的内容,线程中有一堆东西不在catchtry中捕获整个while循环是否需要转到服务器上的lavasoft广告感知程序,并从广告软件扫描中排除您的可执行文件。@SqlSurfer它不是lavasoft-请参阅我在问题后面附加的内容。@KeithNicholas我刚刚尝试移动
try
4行更高,但它是一样的。谢谢,不过我刚刚尝试了TaskScheduler.UnobservedTaskeException,但它没有捕获它。我看不出HttpClient有什么优势,当我将结果直接流式传输到文件时,创建一个字符串是一件很遗憾的事情。我应该使用TaskCreationOptions.LongRunning(尽管它不能解决问题)。这也不能解释为什么内部捕获无法捕获它。@wezten您的错误是关于基础结构的,因此它可能发生在某些系统代码内部,超出范围
try
HttpClient
也可以处理流,您没有阅读文档:
 Task.Factory.StartNew(() => ...)
    .ContinueWith (task => 
    {
       If (task.isFaulted)
       {
           //task.Exception
           //handle the exception from this context
       }
    });
static void Main(string[] args)
{
    try
    {
        string filePath = ConfigurationManager.AppSettings["filePath"],
            folder = ConfigurationManager.AppSettings["folder"];
        Directory.CreateDirectory(folder);
        List<string> urls = File.ReadAllLines(filePath).Take(10000).ToList();

        int urlIX = -1;
        Task.WaitAll(Enumerable.Range(0, 50).Select(x => Task.Factory.StartNew(() =>
          {
              try
              {
                  while (true) // ** was unprotected
                  {
                      int curUrlIX = Interlocked.Increment(ref urlIX);  // ** was unprotected
                      if (curUrlIX >= urls.Count)   // ** was unprotected
                          break;                    // ** was unprotected
                      string url = urls[curUrlIX];  // ** was unprotected
                      try
                      {
                          var req = (HttpWebRequest)WebRequest.Create(url);
                          using (var res = (HttpWebResponse)req.GetResponse())
                          using (var resStream = res.GetResponseStream())
                          using (var fileStream = File.Create(Path.Combine                    (folder, Guid.NewGuid() + url.Substring(url.LastIndexOf('.')))))
                              resStream.CopyTo(fileStream);
                      }
                      catch (Exception ex)
                      {
                          Console.WriteLine("Error downloading img: " + url + "\n" + ex);
                          continue;
                      }
                  } // while
              } // try
          })).ToArray());
    }
    catch
    {
        Console.WriteLine("Something bad happened.");
    }
}