1

我有一个类有许多属性是IntegerSingle。我想以多线程的方式使用这个类,使得这些属性可以用作累加器(该类是报告方案的基础)。所以,我希望能够做这样的事情:线程安全属性

Public Class ReportTotals 
    Property Count As Integer 
    Property Total As Single 
    Property Tax As Single 
    Property Shipping As Single 
    Property ItemsSold As Integer 

    Public Function GetReport() As String 
    ... 
    End Function 
End Class 

Public Function BuildReportData As ReportTotals 

    Dim myReport As New ReportTotals 
    With myReport 
     Parallel.ForEach(UserSales, Sub(userSale) 
       .Count += 1 
       .Total += userSale.Total 
       .Tax += userSale.Tax 
       .Shipping += userSale.Shipping 
       .ItemsSold += userSale.ItemsSold 

       'more complicated stuff and property assignments 

      End Sub) 
    End With 
End Function 

根据我的研究,我知道IntegerSingle是原子,但我不知道这延伸到是一类的整数部分。我不想假设,因为多线程的bug可能会在稍后出现并咬我。

更新:显然,Single是不是线程安全的,所以我必须使用该锁定,但Integer

+0

整数分配是原子,但'.Count + = 1'不是。 'Interlocked.Increment'可以执行一个原子操作,但是如果你想确保两个增量操作是原子的,你可以使用'Lock' – I4V 2013-04-22 20:52:44

+0

你的代码看起来并不会从并行化中获得太多收益(假设它是你的真实代码) 。如果每次迭代都这么简单,我认为并行版本可能会因开销而实际上变慢。 – svick 2013-04-22 21:27:58

+0

每次迭代都不简单。在每次迭代中都有几个LINQ语句和数学处理发生。我只是在做上述事情时注册了近50%的速度(当然,这不包括线程安全性)。 – cjbarth 2013-04-22 21:50:12

回答

1

这听起来像是你想要的是an overload of Parallel.ForEach() that works with local state

localInit委托,您将创建的ReportTotals一个新的实例,并在localFinally你当地ReportTotals的值添加到全局的,锁下。

+0

虽然我认为这种模式使我的代码更难阅读,但它是实现这一点的正确方法,而不是我[做](http:// stackoverflow。COM /一个/271351分之16157588);它可能会更快一点。学习这种新模式非常好,我不需要改变我的类(除了锁对象)就可以做到这一点。 – cjbarth 2013-04-23 03:56:07

3

您可以使用Interlocked.Increment以原子方式增加整数,即使它们是类成员。

+0

但是你不能用属性来做到这一点。 – svick 2013-04-22 21:25:31

+0

根据示例代码,没有理由将它们定义为与公共字段相反的属性 - 它们可公开访问,没有特殊的获取/设置逻辑。 – 2013-04-22 21:39:00

+0

作为个人偏好,我会实现私有Integer成员并使用Interlocked.Increment和公共属性获取器来避免允许任何其他代码修改该值。 – 2013-04-23 00:42:09

0

也许SyncLock是你在找什么。

Public Sub sale(s As String) 
    SyncLock _lock 
      //'thread safe code' 
    End SyncLock 
End Sub 

Public Function BuildReportData as ReportTotals 

Dim myReport As New ReportTotals 
With myReport 
    Parallel.ForEach(UserSales, sale(userSale)) 

End Function 
+0

是的,但是如果将整个方法放在'SyncLock'中,没有理由使用'Parallel.ForEach()',因为它不会实际并行运行。 – svick 2013-04-22 21:34:55

+0

你说得对。对不起,我的错误答案!我会把它留在这里,也许有人会从我错误的假设中学习。 – misleadingTitle 2013-04-22 21:47:22

0

我已经决定这样做,至少要等到有人有更好的想法,是在Parallel.ForEach程序来创建大量ReportTotals对象,并把他们都变成ConcurrentBag。然后,在Parallel.ForEach声明结束后,我使用常规For Each语句将ConcurrentBag中的ReportTotals对象中的所有值累计为一个新的ReportTotals对象,然后返回。

所以我在做这样的事情:

Public Class ReportTotals 
    Property Count As Integer 
    Property Total As Single 
    Property Tax As Single 
    Property Shipping As Single 
    Property ItemsSold As Integer 

    Public Function GetReport() As String 
    ... 
    End Function 
End Class 

Public Function BuildReportData As ReportTotals 

    Dim myReport As New ReportTotals 
    Dim myReports As New ConcurrentBag(Of ReportTotals) 

    Paralle.ForEach(UserSales, Sub(userSale) 
      Dim workingReport As New ReportTotals 
      With workingReport 
       .Count += 1 
       .Total += userSale.Total 
       .Tax += userSale.Tax 
       .Shipping += userSale.Shipping 
       .ItemsSold += userSale.ItemsSold 

       'more complicated stuff and property assignments 

      End With 
     End Sub) 

    For Each report In myReports 
     With myReport 
      .Count += report.Count 
      .Total += report.Total 
      .Tax += report.Tax 
      .Shipping += report.Shipping 
      .ItemsSold += report.ItemsSold 
     End With 
    Next 

    Return myReport 
End Function