c# - 集合被修改了 C# 异常但不知道为什么
问题描述
我在 C# 中有这段代码:
Thread.BeginCriticalRegion();
if(visitedUrls.Contains(url) || visitedUrls.Where( x => x.Contains(root) ).Count() > 150) {
return;
}
else{
visitedUrls.Add(url);
}
Thread.EndCriticalRegion();
它是一个被几个不同进程调用的函数,这就是为什么我(试图)使它成为线程安全的。
异常Collection was modified; enumeration operation may not execute
引发if
在线,但如果我将其保留为
if(visitedUrls.Contains(url)
它工作正常,为什么?
编辑 这是实际的代码:
public void scrapAzienda(String url, String root_url, int depth)
{
if (depth <= 0) return;
var web = new HtmlWeb();
HtmlNode[] nodes = null;
HtmlDocument doc = null;
HtmlNode bodyNode = null;
Thread.BeginCriticalRegion();
if (urlVisitati.Contains(url) || urlVisitati.Where(x => x.Contains(root_url)).Count() > 150)
return;
else
urlVisitati.Add(url);
Thread.EndCriticalRegion();
try
{
doc = web.Load(url, Proxy.getUrl(), Proxy.getPort(), Proxy.getUsername(), Proxy.getPassword());
nodes = doc.DocumentNode.SelectNodes("//a[@href]").ToArray() ?? null;
foreach (HtmlNode item in nodes)
{
Task.Factory.StartNew(() => scrapAzienda(item.Attributes["href"].Value, root_url, depth - 1), TaskCreationOptions.AttachedToParent);
}
GC.Collect();
if (doc != null)
{
bodyNode = doc.DocumentNode.SelectSingleNode("//body");
cercaNumeri(bodyNode.InnerText, url);
cercaEmail(bodyNode.InnerText, url);
}
}
catch (Exception) { }
}
基本上它只是一个网络爬虫。
解决方案
我认为线程是您的全部问题。 Thread.BeginCriticalRegion()
不做你认为它做的事。从文档:
通知主机执行即将进入代码区域,其中线程中止或未处理异常的影响可能会危及应用程序域中的其他任务。
换句话说,它不强制执行线程安全,它只是说“如果这坏了,它就会把所有东西都拿下来!”
你需要的是一个基本的lock
:
lock(someObj)
{
if (urlVisitati.Contains(url) || urlVisitati.Where(x => x.Contains(root_url)).Count() > 150)
{
return;
}
else
{
urlVisitati.Add(url);
}
}
someObj
必须是静态对象。每个线程都需要引用同一个对象。为此,我通常在类级别创建一个基本对象:
private static readonly object SyncLock = new object();
然后,您将 lock 与该对象一起使用:lock(SyncLock)
. 您也可以锁定列表本身,但是,一次只有一个线程可以锁定同步对象。为了帮助防止死锁,理想情况下,您的代码应该是您正在同步的任何对象上的唯一锁定源。你能保证列表类本身的东西不会被自己锁定吗?不用担心,制作自己的同步对象。没什么大不了的。
这是使用lock
. 还有其他方法可以做到这一点。这个应该适合你。
推荐阅读
- html - 将表单数据发送到 pdfkit iframe 文档
- php - 我什么时候应该在 Symfony 4.4 控制器中使用拒绝访问功能?
- json - EasyAdmin 3 (Symfony 5) 中的数组到字符串转换
- c# - 如何在 C# 中更改鼠标的 DoubleClickSpeed?
- signals - 如何处理 NASM elf64 中的信号?
- android - 无法解决:com.esri.arcgisruntime:arcgis-android:100.12.0
- arrays - Laravel 将表示对象数组的字符串导入 JSON 列
- flutter - Flutter,image_cropper,如何以特定比例调整大小
- imagemagick - 如何使用 Node GraphicsMagick/ImageMagick 获取 gif 帧
- linux - 如何将儒略日期转换为公历日期并使用 Shell 脚本从中减去几天