使用Parallel.Foreach时出现意外行为

时间:2016-07-13 09:34:00

标签: c# parallel-processing task-parallel-library parallel.foreach

我有这段代码:

// positions is a List<Position>
Parallel.ForEach(positions, (position) =>
{
    DeterminePostPieceIsVisited(position, postPieces);
});

private void DeterminePostPieceIsVisited(Position position, IEnumerable<Postpieces> postPieces)
{
    foreach (var postPiece in postPieces)
    {
        if (postPiece.Deliverd)
            continue;

            var distanceToClosestPosition = postPiece.GPS.Distance(position.GPS);

            postPiece.Deliverd = distanceToClosestPosition.HasValue && IsInRadius(distanceToClosestPosition.Value);
        }
    }
}

我知道50个帖子必须将属性Deliverd设置为true。但是,在运行此代码时,我会改变结果。有时我得到44,当我再次运行它时,我得到47.结果是执行不同。

当我使用普通的foreach循环运行此代码时,我得到了预期的结果。所以我知道方法DeterminePostPieceIsVisited的实现是正确的。

有人可以向我解释为什么每次执行此代码时使用Parallel foreach会给我不同的结果吗?

2 个答案:

答案 0 :(得分:3)

我认为你已经试图避免竞争,但仍有一个 - 如果两个线程同时检查相同的postPiece,他们可能两者观察Deliverd(原文如此)是false,然后评估它是否已被传递到position(每个线程的一个不同值),并且都试图为{{1}设置一个值 - 我经常猜测,其中一个人会尝试将其设置为Deliverd。简单修复:

false

顺便说一下:

  

当我使用普通的foreach循环运行此代码时,我得到了预期的结果。所以我知道方法private void DeterminePostPieceIsVisited(Position position, IEnumerable<Postpieces> postPieces) { foreach (var postPiece in postPieces) { if (postPiece.Deliverd) continue; var distanceToClosestPosition = postPiece.GPS.Distance(position.GPS); var delivered = distanceToClosestPosition.HasValue && IsInRadius(distanceToClosestPosition.Value); if(delivered) postPiece.Deliverd = true; } } 的实现是正确的。

正确的陈述是“我知道我的实现是正确的用于单线程访问” - 你没有建立的是该方法对于从多个线程调用是安全的。 / p>

答案 1 :(得分:0)

我已用ConcurrentBag<T>解决了我的问题。这是我现在使用的:

var concurrentPostPiecesList = new ConcurrentBag<Postpiece>(postPieces);
Parallel.ForEach(positions, (position) => 
{
    DeterminePostPieceIsVisited(position, concurrentPostPiecesList);
});

private void DeterminePostPieceIsVisited(Position position, ConcurrentBag<Postpieces> postPieces)
{
    foreach (var postPiece in postPieces)
    {
        if (postPiece.Deliverd)
            continue;

        var distanceToClosestPosition = postPiece.GPS.Distance(position.GPS);

        postPiece.Deliverd = distanceToClosestPosition.HasValue && IsInRadius(distanceToClosestPosition.Value);
    }
}