2

请帮我把下面的循环转换成并行循环。我尝试使用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 

回答

2

是的,你的代码肯定不是线程安全的。使用线程安全集合不会使您的代码自动地线程安全,您仍然需要正确使用它们。

您的问题是被称为一个线程后Contains()完成但在此之前Add()Add()可以在另一个线程调用(并且同样可以也可能发生Contains()执行)。

你需要做的是:

  1. 使用锁定(这意味着你不需要再使用一个线程安全的集合);或
  2. 使用类似ConcurrentHashSet的东西。 .Net中没有这样的类,但是您可以使用ConcurrentDictionary(即使它完全不符合您的需求)。而不是你的电话Contains()然后Add(),你可以做Matched.TryAdd(Item, True),其中True只是因为ConcurrentDictionary需要一些价值。
+0

我用另一种方法找到了这两个样本。厌倦改变,但不能使它与我的收藏品和物品一起工作,而不是像这些样品中的物品一样。 Svick,你认为这些会在我的情况下工作吗? http://technet.microsoft.com/subscriptions/dd460699.aspx http://tipsandtricks.runicsoft.com/CSharp/ParallelClass.html – jjarv 2013-04-07 08:11:19

相关问题