Parallel.Foreach中出现意外的线程争用

时间:2012-12-20 18:33:13

标签: c# multithreading parallel-processing task-parallel-library

我尝试使用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正在进行争用,但我在EnvironmentTickCount循环内调用Parallel.Foreach进行了一项实验,并获得了近100%的处理器使用率。

我只能在Core i5上使用45%到50%。

1 个答案:

答案 0 :(得分:2)

这是GC捶打的常见症状。如果不了解你在Expand方法中做了什么,我最好的猜测是这将是你的根本原因。一些共享数据访问也可能是罪魁祸首,无论是通过调用远程系统,还是通过锁定对共享资源的访问。

在您执行任何操作之前,您需要使用分析器或其他工具确定确切原因。不要猜测,因为这会浪费你的时间,不要在这里等待答案,因为没有你的完整程序,它无法回答。正如您从实验中已经知道的那样,Parallel.ForEach中没有任何内容会导致这种情况。