请帮我将以下循环转换为并行循环。我尝试使用Parallel.ForEach和ConcurrentBag而不是HashSet,但是,每次不同的结果时,“匹配”都会返回。
我无法弄明白......是否因为线程安全问题?
关键字列表包含大约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
答案 0 :(得分:2)
是的,您的代码当然不是线程安全的。使用线程安全的集合不会使您的代码自动进行线程安全,您仍然需要正确使用它们。
你的问题是在Contains()
完成之后但在一个线程上调用Add()
之前,Add()
可以在另一个线程上调用(同样可能也会发生而 Contains()
执行。)
您需要做的是:
ConcurrentHashSet
之类的内容。 .Net中没有这样的类,但您可以使用ConcurrentDictionary
代替(即使它不完全符合您的需求)。您可以执行Contains()
,而Add()
只是因为Matched.TryAdd(Item, True)
需要一些价值,而不是您True
和ConcurrentDictionary
的来电。