当前位置:编程学习 > asp >>

关于Paralle.For的求和问题

.NET4中加入了并行机制——所谓并行就是同时开辟若干个线程来进行计算。这些线程由于都是互相独立的,所以在进行一些分布式(比如各自不同的工作)的时候是非常简单,不过要把这些处理结果汇总起来却不是那么容易——下面来看一个非常简单的例子(求1~1000的和)。
如果你尝试使用以下的代码计算,恐怕令你大跌眼镜!
[C#]
int sum = 0;
Parallel.For(0, 1000,i => {sum+=i;});
[VB.NET]
Dim sum As Integer = 0
Parallel.[For](0, 1000, Function(i)
sum += i)
究其原因就是.NET会默认开辟一些线程同时进行“sum+=i”的计算。那么由于sum被这些线程同时使用,往往是一个线程还没有处理完毕,另外一个线程又介入了,自然无法得到正确结果了。
解决这个问题的办法有许多:
【一】分解法:
所谓分解法,就是针对“同一个变量”被不同线程“共享”这一诟病而提出的。——也就是说,把1~1000求和分成若干块进行处理(等于给每一个线程分配了不同的内存)。最后把分布计算的结果进行累计汇总即可。结果如下:
[C#]
int[] numbers = Enumerable.Range(1, 1000).ToArray();
int[] values=new int[4];
int sum = 0;
Parallel.For(0, 4, i => { values[i] = new Program().GetTotal(i * 250, 250, numbers); });
sum = values.Sum();
Console.WriteLine(sum);
[VB.NET]
 
Dim numbers As Integer() = Enumerable.Range(1, 1000).ToArray()
Dim values As Integer() = New Integer(3) {}
Dim sum As Integer = 0
Parallel.[For](0, 4, Function(i)
values(i) = New Program().GetTotal(i * 250, 250, numbers))
sum = values.Sum()
Console.WriteLine(sum)
 
【二】使用静态变量
静态变量本身就具备“同步”的能力(这种例子在简单工厂中也可以窥见)。所以这里显然可以使用,代码如下:
[C#]
 
public class Program
        {
            static int sum = 0;
            static void Main(string[] args)
            {
                Parallel.For(1, 1001, i => {sum+=i;});
                Console.WriteLine(sum);
            }
        }
 
[VB.NET]
 
Public Class Program
    Shared sum As Integer = 0
    Private Shared Sub Main(args As String())
        Parallel.[For](1, 1001, Sub(i)
        sum += i)
        Console.WriteLine(sum)
    End Sub
End Class
 
【三】使用volatile变量(一般编译器为了优化性能,往往把常用的变量复制一份到寄存器中,然后直接对寄存器进行操作,等到操作完毕之后再写回原来的内存中。多线程会导致寄存器中的变量不同步,所以结果也不对。而volatile就是告诉编译器直接从内存中读取那个数字进行操作,而不是拷贝到寄存器之后处理):
[C#]
 
public class Program
        {
            volatile int sum = 0;
            public void ShowResult()
            {
                Parallel.For(1, 1001, i => { sum+=i; });
                Console.WriteLine(sum);
            }

            static void Main(string[] args)
            {
                Program p = new Program();
                p.ShowResult();
            }
        }
 
[VB.NET,由于VB.NET没有此特性,所以使用Thread.VolitaleRead每次读取最新的数值后进行累加]
 
Public Class Program
    Dim sum As Integer = 0
    Public Sub ShowResult()
        Parallel.For(1, 1001, Sub(i)
                                  Thread.VolatileRead(sum)  '总是读取最新数值
                                  sum = sum + i
                                  Thread.Sleep(2)
                              End Sub)
        Console.WriteLine(sum)
    End Sub

    Public Shared Sub Main(args As String())
        Dim p As New Program()
        p.ShowResult()
    End Sub
End Class
 
【四】使用lock(锁住一个变量,然后直到该线程操作完毕自动释放变量,另外一个线程进来操作……如此反复而已):
[C#]
 
 public class Program
        {
            int sum = 0;
            public void ShowResult()
            {
                object obj = new object();
                Parallel.For(1, 1001, i => { lock (obj) { sum += i; Thread.Sleep(10); } });
                Console.WriteLine(sum);
            }

            static void Main(string[] args)
            {
                Program p = new Program();
                p.ShowResult();
            }
        }
 
[VB.NET]
 
Public Class Program
    Private sum As Integer = 0
    Private obj As New Object
    Public Sub ShowResult()
        Dim obj As New Object()
        Parallel.For(1, 1001, Sub(i)
 

补充:Web开发 , ASP.Net ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,