Parallel.for Loop - 结果因每次点击而异。为什么会发生这种情况?

时间:2015-08-13 13:11:33

标签: vb.net visual-studio-2012

以下parallel.for循环使用长度为300000位的比特阵列数据。并且数据固定不变。因此,无论执行“check”函数多少次,生成的结果“Count_of_Found_Pattern1”必须相同

但是,问题是“Count_of_Found_Pattern1”&的值。每次执行“check”功能时,“Count_of_Found_Pattern2”都会产生不同的值。我做错了什么?

当我使用少量位(大约16位而不是300位)检查它时,它会产生良好的结果。但是,当比特长度更长时,它会产生一团糟。

例如:

第一次执行 - > Count_of_Found_Pattern1 = 150526,Count_of_Found_Pattern2 = 97855

第二次执行 - > Count_of_Found_Pattern1 = 45855,Count_of_Found_Pattern2 = 187562

问候!

Private Function check() 

        Dim Count_of_Found_Pattern1 As Int64 = 0
        Dim Count_of_Found_Pattern2 As Int64 = 0
        Dim stopwatch As New Stopwatch


        stopwatch.Start()

        Dim Current_Position1 As Int64 = 0 
        Dim Current_Position2 As Int64 = 1

        Parallel.For(0, lastbitarrayover2, Sub(countbits, loopstate)


                                               If BitArray(Current_Position1) = False And BitArray(Current_Position2) = True Then

                                                   Count_of_Found_Pattern1 = Count_of_Found_Pattern1 + 1
                                               End If

                                               If BitArray(Current_Position1) = True And BitArray(Current_Position2) = False Then

                                                   Count_of_Found_Pattern1 = Count_of_Found_Pattern1 + 1
                                               End If

                                               If BitArray(Current_Position1) = True And BitArray(Current_Position2) = True Then

                                                   Count_of_Found_Pattern2 = Count_of_Found_Pattern2 + 1

                                               End If

                                               If BitArray(Current_Position1) = False And BitArray(Current_Position2) = False Then

                                                   Count_of_Found_Pattern2 = Count_of_Found_Pattern2 + 1

                                               End If

                                               Current_Position1 = Current_Position1 + 2
                                               Current -Position2 = Current_Position2 + 2
                                               Numer_of_Completed_Iterations = Numer_of_Completed_Iterations + 1

                                           End Sub)


        Numer_of_Completed_Iterations = 0 'reset counter to 0


        stopwatch.Stop()
        TextBox1.Text = stopwatch.Elapsed.ToString

    End Function

2 个答案:

答案 0 :(得分:0)

您是否尝试过System.Threading.Interlocked.Increment以使此线程安全?例如:

If BitArray(Current_Position1) = False And BitArray(Current_Position2) = True Then
    Interlocked.Increment(Count_of_Found_Pattern1)
End If

答案 1 :(得分:0)

当你递增Count_of_Found_Pattern1(或Pattern2)时,首先读取该值,然后递增,然后分配。但是正在执行的线程可以在这三个步骤中发生变化。

Thread 1: Read Count_of_Found_Pattern1
Thread 2: Read Count_of_Found_Pattern1
Thread 2: Increment
Thread 2: Write to Count_of_Found_Pattern1
...
Thread 1: Increment its old value
Thread 1: Write to Count_of_Found_Pattern1

现在Count_of_Found_Pattern1错了。如果线程2控制执行超过一次迭代,那可能是非常错误的。

考虑在PLINQ中尽可能多地做,避免任何全局状态的突变,直到所有线程都加入备份。以下代码假定您对比较BitArray中的相邻条目感兴趣:

Dim counts = Enumerable.
             Range(0, lastbitarrayover2 / 2).Select(Function(i) i * 2).
             AsParallel().
             Aggregate(
                 Function() 
                     ' We're using an object of anonymous type to hold
                     ' the results of the calculation so far.
                     ' Each thread calls this function, so each thread
                     ' gets its own object for holding intermediate results
                     Return New With {.Pattern1 = 0, .Pattern2 = 0, .Iterations = 0}
                 End Function,
                 Function(accumulator, i)
                     ' accumulator is this thread's intermediate-result-holder.
                     ' i is one of the even numbers from our big set of even numbers.
                     ' Each thread will call this function many times, building up its own accumulator object
                     ' the four conditionals from your code reduce to this If block
                     If (BitArray(i) = BitArray(i + 1)) Then
                         accumulator.Pattern2 += 1
                     Else
                         accumulator.Pattern1 += 1
                     End If
                     accumulator.Iterations += 1
                     Return accumulator
                 End Function,
                 Function(acc1, acc2)
                     ' Once each thread has built up its own accumulator object,
                     ' this function makes a new accumulator object that
                     ' combines the results from two threads.
                     ' This is called repeatedly until all the threads' results
                     ' have been combined.
                     Return New With {
                         .Pattern1 = acc1.Pattern1 + acc2.Pattern1,
                         .Pattern2 = acc1.Pattern2 + acc2.Pattern2,
                         .Iterations = acc1.Iterations + acc2.Iterations}
                 End Function,
                 Function(acc) 
                     ' The last function here is supposed to take the combined results
                     ' and turn them into what you ultimately want to use.
                     ' Since the combined results are already in the form we want,
                     ' we'll just use the "identity" function here: it returns its
                     ' argument unchanged
                     Return acc
                 End Function)

Count_of_Found_Pattern1 = counts.Pattern1
Count_of_Found_Pattern2 = counts.Pattern2
Number_of_Completed_Iterations = counts.Iterations

这可能看起来很多,但它确实不是太糟糕。主要的是我们给每个线程一组自己的变量来处理;这样我们就不必担心我在答案顶部列出的问题了。然后,我们结合每个线程最后完成的工作。