C# catch未捕获多线程错误
下面是一个完整的控制台程序,它再现了我遇到的一个奇怪错误。程序读取包含远程文件URL的文件,每行一个。它启动了50个线程来下载它们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"];
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.");
}
}