2

请帮我将以下循环转换为并行循环。我尝试使用 Parallel.ForEach 和 ConcurrentBag 而不是 HashSet,但奇怪的是“Matched”每次返回不同的结果。

我想不通...是因为线程安全问题吗?

关键字列表包含大约 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
4

1 回答 1

3

是的,您的代码当然不是线程安全的。使用线程安全集合不会使您的代码自动成为线程安全的,您仍然需要正确使用它们。

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

您需要做的是:

  1. 使用锁定(这意味着您不再需要使用线程安全集合);或者
  2. 使用类似的东西ConcurrentHashSet。.Net 中没有这样的类,但您可以ConcurrentDictionary改用(即使它不完全符合您的需求)。而不是你调用Contains()then Add(),你可以做Matched.TryAdd(Item, True), where Trueis there 只是因为ConcurrentDictionary需要一些价值。
于 2013-04-05T14:40:57.920 回答