使用TPL的Parallel.ForEach时跟踪进度

跟踪以下进度的最佳方式是什么?

long total = Products.LongCount(); long current = 0; double Progress = 0.0; Parallel.ForEach(Products, product => { try { var price = GetPrice(SystemAccount, product); SavePrice(product,price); } finally { Interlocked.Decrement(ref this.current); }}); 

我想将进度变量从0.0更新为1.0(当前/总)但我不想使用会对并行性产生负面影响的任何内容。

Jon的解决方案很好,如果你需要像这样的简单同步,你的第一次尝试应该几乎总是使用lock 。 但是,如果你测量锁定减慢了太多的东西,你应该考虑使用像Interlocked这样的东西。

在这种情况下,我将使用Interlocked.Increment增加当前计数,并将Progress更改为属性:

 private long total; private long current; public double Progress { get { if (total == 0) return 0; return (double)current / total; } } … this.total = Products.LongCount(); this.current = 0; Parallel.ForEach(Products, product => { try { var price = GetPrice(SystemAccount, product); SavePrice(product, price); } finally { Interlocked.Increment(ref this.current); } }); 

此外,您可能想要考虑如何处理exception,我不确定以exception结束的迭代应该算作完成。

由于您只是进行了一些快速计算,因此通过锁定适当的对象来确保primefaces性:

 long total = Products.LongCount(); long current = 0; double Progress = 0.0; var lockTarget = new object(); Parallel.ForEach(Products, product => { try { var price = GetPrice(SystemAccount, product); SavePrice(product,price); } finally { lock (lockTarget) { Progress = ++this.current / total; } }}); 

一种不使用身体任何阻塞的解决方案:

 long total = Products.LongCount(); BlockingCollection states = new BlockingCollection(); Parallel.ForEach(Products, () => { MyState myState = new MyState(); states.Add(myState); return myState; }, (i, state, arg3, myState) => { try { var price = GetPrice(SystemAccount, product); SavePrice(product,price); } finally { myState.value++; return myState; } }, i => { } ); 

然后,要访问当前进度:

 (float)states.Sum(state => state.value) / total