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);
}
}
如何确认循环运行的次数正确?
循环运行的次数正确,但您无法控制对共享对象的访问。