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