在Parallel.Foreach中出现意外的线程争用
本文关键字:意外 线程 争用 Parallel Foreach | 更新日期: 2023-09-27 17:49:17
我尝试使用Parallel.Foreach
实现以下算法。我认为这将是微不足道的并行,因为它没有同步问题。它基本上是一个蒙特卡洛树搜索,我在并行中探索每个子节点。蒙特卡罗的东西并不重要,你只需要知道我有一个方法可以在某棵树上工作,我用Parallel.Foreach
在根结点上调用它。下面是执行并行调用的代码片段。
public void ExpandParallel(int time, Func<TGame, TGame> gameFactory)
{
int start = Environment.TickCount;
// Creating all of root's children
while (root.AvailablePlays.Count > 0)
Expand(root, gameInstance);
// Create the children games
var games = root.Children.Select(c =>
{
var g = gameFactory(gameInstance);
c.Play.Apply(g.Board);
return g;
}).ToArray();
// Create a task to expand each child
Parallel.ForEach(root.Children, (tree, state, i) =>
{
var game = games[i];
// Make sure we don't waste time
while (Environment.TickCount - start < time && !tree.Completed)
Expand(tree, game);
});
// Update (reset) the root data
root.Wins = root.Children.Sum(c => c.Wins);
root.Plays = root.Children.Sum(c => c.Plays);
root.TotalPayoff = root.Children.Sum(c => c.TotalPayoff);
}
Func<TGame, TGame>
代表是一个克隆工厂,因此每个子节点都有自己的游戏状态克隆。如果需要,我可以解释Expand
方法的内部,但我可以保证它只访问当前子树和游戏实例的状态,并且在任何这些类型中都没有static
成员。我认为可能是Environment.TickCount
引起了争论,但是我在Parallel.Foreach
循环中运行了一个实验,只是调用EnvironmentTickCount
,并且获得了接近100%的处理器使用率。
我在酷睿i5上的使用率只有45%到50%。
这是GC抖动的常见症状。在不了解更多关于Expand方法内部的操作的情况下,我最好的猜测是这将是您的根本原因。也有可能一些共享数据访问也是罪魁祸首,要么是通过调用远程系统,要么是通过锁定对共享资源的访问。
在您做任何事情之前,您需要使用分析器或其他工具确定确切的原因。不要猜测,因为这只会浪费你的时间,也不要在这里等待答案,因为没有完整的程序就无法回答。正如您已经从实验中知道的那样,Parallel.ForEach
中没有任何东西会导致这种情况。