关于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 ,