这是一个失败的测试。如何确认循环运行的次数是否正确?
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);
}
答案 0 :(得分:7)
与此同时,你正在运行这些行:
var existing = resultsParallel[val];
resultsParallel[val] = existing + 1;
对于任何特定的val
值,无法保证只有一个线程/任务同时运行这些行。因此,两个线程可以读取值2,添加1,并存储值3.您需要使用线程安全的方法来累积总数。
E.g。您可以使用Parallel.For
的重载,允许每个线程单独构建自己的结果副本,然后进行最后的组合步骤,以便计算总结果:
public static ParallelLoopResult For<TLocal>(
long fromInclusive,
long toExclusive,
Func<TLocal> localInit,
Func<long, ParallelLoopState, TLocal, TLocal> body,
Action<TLocal> localFinally
)
答案 1 :(得分:1)
您正在使用非线程安全的哈希表实现。因此,你只能证明你犯了一个错误。请改用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);
});
答案 2 :(得分:0)
您可以使用信号量序列化对resultsParallel的并发访问,resultsParallelForEach:
公共类示例 { public static Random Randomator {get;组; } public const 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);
}
}