C# Parallel.For和Parallel.ForEach未运行到结束

C# Parallel.For和Parallel.ForEach未运行到结束,c#,task-parallel-library,parallel.foreach,C#,Task Parallel Library,Parallel.foreach,这是一个失败的测试。如何确认循环运行的次数正确 public Random Randomator { get; set; } public const int TimesToRun = 1000000; [TestMethod] public void ThrowTheDice() { Randomator = new Random(); var resultsParallel = new Dictionary<i

这是一个失败的测试。如何确认循环运行的次数正确

    public Random Randomator { get; set; }
    public const int TimesToRun = 1000000;

    [TestMethod]
    public void ThrowTheDice()
    {
        Randomator = new Random();

        var resultsParallel = new Dictionary<int, int>
        {
            {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}
        };

        var resultsParallelForEach = new Dictionary<int, int>
        {
            {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}
        };

        var stopwatch = new Stopwatch();
        stopwatch.Start();
        Parallel.For(0, TimesToRun, ctr =>
        {
            var val = ThrowDice();
            if (!resultsParallel.ContainsKey(val))
                throw new ArgumentOutOfRangeException();

            var existing = resultsParallel[val];
            resultsParallel[val] = existing + 1;
        });

        stopwatch.Stop();
        var parallelTime = stopwatch.Elapsed;

        stopwatch = new Stopwatch();
        stopwatch.Start();
        var numbers = Enumerable.Range(0, TimesToRun);
        Parallel.ForEach(numbers, ctr =>
        {
            var val = ThrowDice();
            if (!resultsParallel.ContainsKey(val))
                throw new ArgumentOutOfRangeException();

            var existing = resultsParallelForEach[val];
            resultsParallelForEach[val] = existing + 1;
        });

        stopwatch.Stop();
        var parallelForEachTime = stopwatch.Elapsed;

        var parallelTotal = resultsParallel.Sum(x => x.Value);
        var parallelForEachTotal = resultsParallelForEach.Sum(x => x.Value);

        Assert.AreEqual(parallelTotal, TimesToRun);
        Assert.AreEqual(parallelForEachTotal, TimesToRun);
    }

    public int ThrowDice()
    {
        return Randomator.Next(1, 7);
    }
公共随机随机化器{get;set;}
公共常数int TimesToRun=1000000;
[测试方法]
公共无效税()
{
随机数=新随机数();
var resultspallel=新字典
{
{1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}
};
var resultspallelforeach=新字典
{
{1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}
};
var stopwatch=新秒表();
秒表。开始();
Parallel.For(0,TimesToRun,ctr=>
{
var val=ThrowDice();
如果(!resultspallel.ContainsKey(val))
抛出新ArgumentOutOfRangeException();
var existing=结果并行[val];
resultsParallel[val]=现有+1;
});
秒表;
var parallelTime=秒表。已用时间;
秒表=新秒表();
秒表。开始();
变量编号=可枚举范围(0,TimesToRun);
Parallel.ForEach(数字,中心=>
{
var val=ThrowDice();
如果(!resultspallel.ContainsKey(val))
抛出新ArgumentOutOfRangeException();
var existing=resultsParallelForEach[val];
resultsParallelForEach[val]=现有+1;
});
秒表;
var PARALLEFOREACHTIME=秒表已用时间;
var parallelTotal=resultsParallel.Sum(x=>x.Value);
var parallelForEachTotal=resultsParallelForEach.Sum(x=>x.Value);
AreEqual(parallelTotal,TimesToRun);
AreEqual(parallelForEachTotal,TimesToRun);
}
公共int ThrowDice()
{
返回随机数。下一步(1,7);
}

同时,您正在运行以下行:

var existing = resultsParallel[val];
resultsParallel[val] = existing + 1;
对于任何特定的
val
value,不能保证只有一个线程/任务同时运行这些行。因此,两个线程可以读取值2,添加1,并存储值3。您需要使用线程安全的方法来累积总数

例如,您可以使用的重载,允许每个线程分别构建自己的结果副本,然后使用最后的合并步骤来计算总结果:

public static ParallelLoopResult For<TLocal>(
    long fromInclusive,
    long toExclusive,
    Func<TLocal> localInit,
    Func<long, ParallelLoopState, TLocal, TLocal> body,
    Action<TLocal> localFinally
)
的公共静态并行循环结果(
从长远来看,
长到排他,
Func localInit,
职能机构,
最后行动
)

您使用的是非线程安全的哈希表实现。因此,你只能证明你犯了错误。使用
ConcurrentDictionary
,这是线程安全的:

var resultsParallel = new ConcurrentDictionary<int, int>();

var stopwatch = new Stopwatch();
stopwatch.Start();
Parallel.For(0, TimesToRun, ctr =>
{
    var val = ThrowDice();
    resultsParallel.AddOrUpdate(val, 1, (key, old) => old + 1);
});
var resultspallel=新的ConcurrentDictionary();
var stopwatch=新秒表();
秒表。开始();
Parallel.For(0,TimesToRun,ctr=>
{
var val=ThrowDice();
结果parallel.AddOrUpdate(val,1,(key,old)=>old+1);
});

您可以使用信号量序列化对resultsParallel、resultsParallelForEach的并发访问:

公开课范例 { 公共静态随机数{get;set;} 公共常数int TimesToRun=1000000

    public static Semaphore semaphore;


    public static void ThrowTheDice()
    {
        Randomator = new Random();

        semaphore = new Semaphore(1, 1);

        var resultsParallel = new Dictionary<int, int>
    {
        {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}
    };

        var resultsParallelForEach = new Dictionary<int, int>
    {
        {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}
    };

        var stopwatch = new Stopwatch();
        stopwatch.Start();
        Parallel.For(0, TimesToRun, ctr =>
        {
            var val = ThrowDice();
            if (!resultsParallel.ContainsKey(val))
                throw new ArgumentOutOfRangeException();

            semaphore.WaitOne();

            var existing = resultsParallel[val];
            resultsParallel[val] = existing + 1;

            semaphore.Release();
        });

        stopwatch.Stop();
        var parallelTime = stopwatch.Elapsed;

        stopwatch = new Stopwatch();
        stopwatch.Start();
        var numbers = Enumerable.Range(0, TimesToRun);
        Parallel.ForEach(numbers, ctr =>
        {
            var val = ThrowDice();
            if (!resultsParallel.ContainsKey(val))
                throw new ArgumentOutOfRangeException();

            semaphore.WaitOne();

            var existing = resultsParallelForEach[val];
            resultsParallelForEach[val] = existing + 1;

            semaphore.Release();
        });

        stopwatch.Stop();
        var parallelForEachTime = stopwatch.Elapsed;

        var parallelTotal = resultsParallel.Sum(x => x.Value);
        var parallelForEachTotal = resultsParallelForEach.Sum(x => x.Value);

        Debug.Assert(parallelTotal == TimesToRun);
        Debug.Assert(parallelForEachTotal == TimesToRun);
    }

    public static int ThrowDice()
    {
        return Randomator.Next(1, 7);
    }
} 
公共静态信号量;
公共静态void ThrowTheDice()
{
随机数=新随机数();
信号量=新信号量(1,1);
var resultspallel=新字典
{
{1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}
};
var resultspallelforeach=新字典
{
{1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}
};
var stopwatch=新秒表();
秒表。开始();
Parallel.For(0,TimesToRun,ctr=>
{
var val=ThrowDice();
如果(!resultspallel.ContainsKey(val))
抛出新ArgumentOutOfRangeException();
WaitOne()信号量;
var existing=结果并行[val];
resultsParallel[val]=现有+1;
semaphore.Release();
});
秒表;
var parallelTime=秒表。已用时间;
秒表=新秒表();
秒表。开始();
变量编号=可枚举范围(0,TimesToRun);
Parallel.ForEach(数字,中心=>
{
var val=ThrowDice();
如果(!resultspallel.ContainsKey(val))
抛出新ArgumentOutOfRangeException();
WaitOne()信号量;
var existing=resultsParallelForEach[val];
resultsParallelForEach[val]=现有+1;
semaphore.Release();
});
秒表;
var PARALLEFOREACHTIME=秒表已用时间;
var parallelTotal=resultsParallel.Sum(x=>x.Value);
var parallelForEachTotal=resultsParallelForEach.Sum(x=>x.Value);
Assert(parallelTotal==TimesToRun);
Assert(parallelForEachTotal==TimesToRun);
}
公共静态整数ThrowDice()
{
返回随机数。下一步(1,7);
}
} 

如何确认循环运行的次数正确?
循环运行的次数正确,但您无法控制对共享对象的访问。