简体   繁体   English

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

[英]Track progress when using TPL's Parallel.ForEach

What is the best way way to track progress in the following 以下是跟踪进度的最佳方法

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);
    }});

I want to update the progress variable from 0.0 to 1.0 (current/total) but i don't want to use anything that would have an adverse effect on the parallelism. 我想将进度变量从0.0更新为1.0(当前/总)但我不想使用会对并行性产生负面影响的任何内容。

Jon's solution is good, if you need simple synchronization like this, your first attempt should almost always use lock . Jon的解决方案很好,如果你需要像这样的简单同步,你的第一次尝试应该几乎总是使用lock But if you measure that the locking slows things too much, you should think about using something like Interlocked . 但是,如果你测量锁定会减慢过多的速度,你应该考虑使用Interlocked这样的东西。

In this case, I would use Interlocked.Increment to increment the current count, and change Progress into a property: 在这种情况下,我将使用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);
    }
});

Also, you might want to consider what to do with exceptions, I'm not sure that iterations that ended with an exception should be counted as done. 此外,您可能想要考虑如何处理异常,我不确定以异常结束的迭代应该计为完成。

Since you are just doing a few quick calculations, ensure atomicity by locking on an appropriate object: 由于您只是进行了一些快速计算,因此通过锁定适当的对象来确保原子性:

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;
        }
    }});

A solution without using any blocking in the body: 一种不使用身体任何阻塞的解决方案:

long total = Products.LongCount();
BlockingCollection<MyState> states = new BlockingCollection<MyState>();

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 => { }
);

Then, to access the current progress: 然后,要访问当前进度:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM