.NET任务和字典 - 写得太快了

时间:2015-03-17 19:43:21

标签: .net vb.net multithreading dictionary task

我正在使用.NET任务来获得多线程的优势,但实现起来并不容易。

为了证明我遇到的问题,我写了一个我实际代码的简化版本。

执行代码后,您意识到最终的AllResults字典确实具有KeyValuepairs(Nothing,Nothing) - 这是不可能添加的。

在思考了一下之后,我想我发现了为什么会发生这种情况,但我找不到解决方案。

我猜这种情况正在发生,因为子任务正在将主键级字典“AllResults”中的keyvaluepairs添加得太快 - 当字典达到它的大小时,不允许它分配更多空间。 据我所知,当字典变满时,字典会自动调整为当前大小的两倍。但我想由于使用任务(在其他线程上运行),在调整自身大小时它会获得两倍以上,这会导致Nothing(null)元素。

.NET必须尝试通过添加元素(无,无)来防止内存访问错误(可能是蓝屏)。实际上,这是一个很好的行为,并证明了Dictionaries如何在.NET中编写得很好。它可能会跳过添加但我们可能不知道会导致数据丢失的错误。

但是,如何解决这个问题?

谢谢你们。

Imports System.Threading.Tasks

Module Module1

    Public AllResults As Dictionary(Of Integer, SomeClass) 'this is the issue

    Public Class SomeClass

        Public Property ID As Integer
        Public Property Name As String

        Public ADictionary As Dictionary(Of Integer, Integer) = New Dictionary(Of Integer, Integer)

        Public subClasses As Dictionary(Of Integer, SomeClass)

        Public Sub New()

        End Sub

    End Class

    Sub Main()

        AllResults = New Dictionary(Of Integer, SomeClass)

        Dim Classes As Dictionary(Of Integer, SomeClass) = New Dictionary(Of Integer, SomeClass)
        Classes.Add(10, New SomeClass With {.ID = 1, .Name = "10"})
        Classes.Add(20, New SomeClass With {.ID = 2, .Name = "20"})
        Classes.Add(40, New SomeClass With {.ID = 3, .Name = "40"})
        Classes.Add(80, New SomeClass With {.ID = 4, .Name = "80"})
        Classes.Add(160, New SomeClass With {.ID = 5, .Name = "160"})
        Classes.Add(320, New SomeClass With {.ID = 6, .Name = "320"})
        Classes.Add(640, New SomeClass With {.ID = 7, .Name = "640"})
        Classes.Add(1280, New SomeClass With {.ID = 8, .Name = "1280"})

        Dim MainTasks(Classes.Count - 1) As Task

        Dim MTX As Integer = 0

        Dim Depth As Integer = 3

        For Each sc As SomeClass In Classes.Values
            MainTasks(MTX) = Task.Factory.StartNew(Sub()
                                                       DoCalculation(sc, Depth)
                                                   End Sub)
            MTX += 1
        Next

        Task.WaitAll(MainTasks)

        Console.WriteLine("Completed.")

        Dim IE As IOrderedEnumerable(Of KeyValuePair(Of Integer, SomeClass))


        IE = AllResults.OrderBy(Function(v) v.Value.ID)


        For Each vkvp As KeyValuePair(Of Integer, SomeClass) In IE
        Next


        Console.ReadLine()

    End Sub


    Public Function DoCalculation(inputClass As SomeClass, depth As Integer) As Dictionary(Of Integer, SomeClass)

        'do some work here

        Dim newID As Integer, newClass As SomeClass

        inputClass.subClasses = New Dictionary(Of Integer, SomeClass)

        For x As Integer = 10 To 20
            newID = Integer.Parse(inputClass.ID.ToString + x.ToString)
            newClass = CloneClass(inputClass)
            newClass.ID = newID
            inputClass.subClasses.Add(x, newClass)
        Next

        If depth > 0 Then

            Dim SubTasks(inputClass.subClasses.Count - 1) As Task
            Dim STX As Integer = 0

            For Each c As SomeClass In inputClass.subClasses.Values
                AllResults.Add(c.ID, c)
                SubTasks(STX) = Task.Factory.StartNew(Sub()
                                                          DoCalculation(c, depth - 1)
                                                      End Sub, TaskCreationOptions.AttachedToParent)
                STX += 1
            Next

            Task.WaitAll(SubTasks)

        End If

        Return inputClass.subClasses

    End Function

    Public Function CloneClass(inputClass As SomeClass) As SomeClass

        Dim newClass As SomeClass = New SomeClass

        newClass.Name = "Clone of " + inputClass.Name

        newClass.ADictionary = New Dictionary(Of Integer, Integer)(inputClass.ADictionary)

        Return newClass

    End Function

End Module

1 个答案:

答案 0 :(得分:3)

Dictionary不是线程安全的集合(默认情况下,所有.NET对象都不是,除非文档另有说明)。

这意味着当从多个线程同时使用时,它没有被编程为正常工作。

您必须:

  • 使用同步原语,例如锁定所有AllResults用法。
  • 使用线程安全的集合,查看System.Collections.Concurrent