关于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)
synclock obj
sum = sum + i
end synclock
end sub)
console.writeline(sum)
end sub
end class
module m
sub main()
dim p as new program
p.showresult()
end sub
end module
【五】使用internlock函数:
[c#]
public class program
{
int sum = 0;
public void showresult()
{
parallel.for(1, 1001, i => { interlocked.add(ref sum, i); });
console.writeline(sum);
}
static void main(string[] args)
{
program p = new program();
p.showresult();
}
}
[vb.net]
public class program
private sum as integer = 0
public sub showresult()
parallel.[for](1, 1001, sub(i)
interlocked.add(sum, i)
end sub)
console.writeline(sum)
end sub
shared sub main(args as string())
dim p as new program()
p.showresult()
end sub
end class
摘自 serviceboy