Parallel.ForEach每次给出不同的结果
问题描述:
请帮我把下面的循环转换成并行循环。我尝试使用Parallel.ForEach和ConcurrentBag而不是HashSet,但是最重要的是“Matched”每次返回不同的结果。Parallel.ForEach每次给出不同的结果
我找不出来......是因为线程安全问题吗?
关键字列表包含大约500个唯一字符串,每个长度为1-3个单词。
项目包含约10000项。
原始代码:
Dim Items As IEnumerable(Of Item) = Db.Items.GetAll
Dim Keywords As HashSet(Of String)
Dim Matched As HashSet(Of Item)
For Each Item In Items
For Each Keyword In Keywords
If Regex.IsMatch(Headline, String.Format("\b{0}\b", Keyword), RegexOptions.IgnoreCase Or RegexOptions.CultureInvariant) Then
If Not Matched.Contains(Item) Then
Matched.Add(Item)
End If
End If
Next
Next
尝试将其转换为
Dim Items As IEnumerable(Of Item) = Db.Items.GetAll
Dim Keywords As HashSet(Of String)
Dim Matched As Concurrent.ConcurrentBag(Of Item)
Threading.Tasks.Parallel.ForEach(Of Item)(Items, Sub(Item)
For Each Keyword In Keywords
If Regex.IsMatch(Item.Title, String.Format("\b{0}\b", Keyword), RegexOptions.IgnoreCase Or RegexOptions.CultureInvariant) Then
If Not Matched.Contains(Item) Then
Matched.Add(Item)
End If
Continue For
End If
Next
End If
答
是的,你的代码肯定不是线程安全的。使用线程安全集合不会使您的代码自动地线程安全,您仍然需要正确使用它们。
您的问题是被称为一个线程后Contains()
完成但在此之前Add()
,Add()
可以在另一个线程调用(并且同样可以也可能发生而Contains()
执行)。
你需要做的是:
- 使用锁定(这意味着你不需要再使用一个线程安全的集合);或
- 使用类似
ConcurrentHashSet
的东西。 .Net中没有这样的类,但是您可以使用ConcurrentDictionary
(即使它完全不符合您的需求)。而不是你的电话Contains()
然后Add()
,你可以做Matched.TryAdd(Item, True)
,其中True
只是因为ConcurrentDictionary
需要一些价值。
我用另一种方法找到了这两个样本。厌倦改变,但不能使它与我的收藏品和物品一起工作,而不是像这些样品中的物品一样。 Svick,你认为这些会在我的情况下工作吗? http://technet.microsoft.com/subscriptions/dd460699.aspx http://tipsandtricks.runicsoft.com/CSharp/ParallelClass.html – jjarv 2013-04-07 08:11:19