【问题标题】:Possible race condition in Parallel.ForeachParallel.Foreach 中可能的竞争条件
【发布时间】:2013-11-11 11:38:48
【问题描述】:

我有以下 vb.net 代码,但我不确定这里是否存在竞争条件。

在我的测试中,我没有看到竞争条件的证据。

Public Class Worker
    Private _List As New List(Of clsTag)
    ' assume that we added tag objects to list defined above ....
    Public Sub Main()
        Dim tagCol As IEnumerable = _List 
        Parallel.ForEach(tagCol.Cast(Of Object)(), Sub(TagObj As clsTag)
            TagObj.CheckLogicValue()
        End Sub)
    End Sub
End Class

Public Class clsTag 
    Public Sub CheckLogicValue()
        ...
        glbObject.CalcSomething(Me)
        ...
    End Sub
End Class

Public Class SharedObject
    Public Function CalcSomething(ByRef objTag As clsTag) As Decimal
        ' Do some calculation based on objTag and also modify objTag status here!
    End Function
End Class

其中glbObject是一个全局对象,是SharedObject类的一个实例,函数CalcSomething获取TagObj ByRef并修改这个对象。 安全吗?

关于CalcSomething 的作用:

  • 它遍历所有标签的列表(即_List)并从中读取(不进行任何修改)。
  • 根据在列表中找到的内容计算某个表达式,该表达式既不会更改标记对象也不会更改列表

根据计算结果(也是函数返回的结果),tagObject 的一个字段正在更新,该字段通过 ByRef 传递,所以它是这样的:

Public Class SharedObject
     Public Function CalcSomething(ByRef objTag As clsTag) As Decimal
         ' Do some calculation based on objTag and also modify objTag status here!
         if (...) then
             objTag.ItemBroken = False
         EndIf
     End Function
End Class

【问题讨论】:

  • CalcSomething 里面的代码在这里很重要。例如,我们需要知道它是否正在访问任何状态。
  • 恐怕代码太具体了,没人愿意费心去理解。我将编辑我的主要帖子并添加一些有关它的信息

标签: .net vb.net parallel.foreach


【解决方案1】:
  1. 如果CalcSomething 中的代码修改(甚至使用)某些状态变量,而不是包含在传递的参数中,那么您确实有一个明确的竞争条件。
  2. 即使您不访问参数以外的任何内容,如果您正在更改它,如果同一个对象在集合中被使用两次,例如

    ,仍然有可能获得竞争条件
    Dim _tag = New clsTag With {...}
    _List.Add(_tag);
    _List.Add(_tag);
    

请注意,除非我们确切知道代码在做什么,否则不可能有明确的答案。
如果仅读取全局状态,并且您绝对确定该状态在方法执行期间不会更改,那么您应该免受全局值竞争条件的影响。
此外,如果.ItemBroken(或任何其他设置的字段/属性)未在计算本身中使用,而只是作为某些(可重复)检查的结果设置,您可以合理地确定您不会获得比赛条件。

另一方面,Paralel.For 不会神奇地加快您的处理速度,而且会使代码更难理解。您确定一个简单的顺序 Foreach 不会一样快,同时更易于理解和调试吗?

For Each TagObj As clsTag In tagCol
    TagObj.CheckLogicValue()
Next

【讨论】:

  • 我正在尝试提高不是我编写的代码的性能。原始代码是一个封装在 Synclock 中的顺序循环,需要很长时间。我想通过删除同步时钟并添加并行性来加快速度。我认为编写该代码的人担心该集合会被访问它的软件中的其他线程更改
  • 这让我想,也许我应该保持它的顺序,并删除同步时钟。但是,如果其他线程在我迭代它时更改了集合,我仍然可能会遇到问题。在这种情况下,最好的方法是什么?
  • 这取决于你的实际情况。改变收藏是可能的还是不可能的?无论如何,一个简单的锁定不应该对性能产生太大的不利影响。您是否实际上锁定了一些全局状态,这将迫使其他线程等待?您是否分析了代码以发现瓶颈实际上不在代码本身?
  • 锁在一个全局变量上。花费更多时间后,我看不到可能会更改集合的线程。所以我认为解除锁是安全的。现在剩下的问题只是关于将顺序循环转换为并行循环的风险
猜你喜欢
  • 1970-01-01
  • 2013-04-13
  • 2017-07-17
  • 2021-12-06
  • 1970-01-01
  • 2019-09-09
  • 1970-01-01
  • 2015-04-18
  • 2013-05-06
相关资源
最近更新 更多