线程嵌套的foreach循环?

时间:2009-08-07 09:28:42

标签: c# multithreading foreach

Helllo!

我正在尝试构建一个程序,使用强力计算各种项目的最佳分数。我相信,借助线程,在具有多核CPU的系统上可以提高计算速度。如我错了请纠正我。但是我如何实现这个目标呢?

这是我的代码的一部分,它可以很好地完成工作(没有线程)。

private double _bestScore = 0.0;

private void BruteForce()
{
  foreach (Stats head in _heads)
    foreach (Stats chest in _chests)
      foreach (Stats leg in _legs)
        foreach (Stats feet in _feets)
        {
          int stamina = head.sta + chest.sta + leg.sta + feet.sta;
          int power = head.power + chest.power + leg.power + feet.power;
          int armor = head.armor + chest.armor + leg.armor + feet.armor;
          int hit = head.hit + chest.hit + leg.hit + feet.hit;

          double total = stamina * _scaleSta + power * _scalePower + armor * _scaleArmor;

          if (total > _bestScore && hit >= 100)
          {
            _bestScore = total;

            // Store best setup for output when done with bruteforce
            _bestHead = head;
            _bestChest = chest;
            _bestLeg = leg;
            _bestFeet = feet;
          }
        }
}

那么,无论如何都要通过线程来改善这一点吗?

编辑:修正了拼写错误并更新为包含其他统计信息。命中必须达到100才能成为“最佳得分”的一部分。这样做我不能先检查每个插槽,以尝试找到最好的装备。命中是一个非常重要的数据,达到100,但100之后的每一点都是无用的。 此外,我的代码的这一部分有更多的foreach循环(28而不是4)。所以迭代次数很多。但是,每个列表(_heads,_chests等)通常最多包含2个项目。

4 个答案:

答案 0 :(得分:3)

如果您想要添加多线程的简单方法,请查看Parallel Extensions for .NET。出于性能原因,您只需要在最外层循环上调用Parallel.For()。

答案 1 :(得分:1)

我很确定会这样做,没有任何测试数据或编译器可以让我不是100%自信:

private void BestItems()
{
    _bestHead = GetBestItem(_heads);
    _bestChest = GetBestItem(_chests);
    _bestLeg = GetBestItem(_legs);
    _bestFeet = GetBestItem(_feets);
}


private Stats GetBestItem(List<Stats> items)
{
    double best = 0.0;
    Stats result = null;

    foreach stats item in items
    {
        double total = item.stamina * _scaleSta + item.power * _scalePower + item.armor * _scaleArmor;

        if (total > best)
        {
            result = item;
        }
    }

    return result;
}

修改

步骤:

  1. 按照最重要的统计信息(最小的第一个)
  2. 的顺序为每个广告位创建一个列表
  3. 使用某种加权循环查找满足您的命中等级的最小匹配值。 (yuo将需要这个插槽用于我的下一步)
  4. 对于每个插槽选择项目,其最佳统计数据符合该位置最小命中率。
  5. 你将需要一个接一个的循环,但它比2 ^ 28更好我认为:p

    Edit2:再次,这里仍然没有编译器......但这可能有效。你最终会得到一桶装的线程......

    用于线程加入和等待see here (msdn)(查看互斥锁,监视器,ManualResetEvent,AutoResetEvent)

    private void BruteForce()
    {
    
      var threads = new List<Thread>;
      foreach (Stats head in _heads)
        foreach (Stats chest in _chests)
          foreach (Stats leg in _legs)
            foreach (Stats feet in _feets)
            {
                if (threads.Count <= 2)
    
    
                thread worker = new thread(addressof Process, new object() {head, chest, leg, feet, ...});
                worker.start();
                threads.add(worker);
            }
    
        foreach (Thread t in threads)
            t.join();  //this might not be the best as it might make the main thread wait for each thread one after the other, not when all finished.  A manual/auto reset is probably better here.
    }
    
    
    private void Process(params...)
    {
    
        int stamina = head.sta + chest.sta + leg.sta + feet.sta;
        int power = head.power + chest.power + leg.power + feet.power;
        int armor = head.armor + chest.armor + leg.armor + feet.armor;
        int hit = head.hit + chest.hit + leg.hit + feet.hit;
    
        double total = stamina * _scaleSta + power * _scalePower + armor * _scaleArmor;
    
        lock _bestscore
        {
            if (total > _bestScore && hit >= 100)
            {
                _bestScore = total;
    
                // Store best setup for output when done with bruteforce
                _bestHead = head;
                _bestChest = chest;
                _bestLeg = leg;
                _bestFeet = feet;
            }
        }
    }
    

    编辑4:猜猜谁还没有靠近他的编译器? 根据这一点,应该确保你在任何时候只有2个线程存活。

    var threads = new Dictionary<Guid, Thread>;
    
    private void BruteForce()
    {
      foreach (Stats head in _heads)
        foreach (Stats chest in _chests)
          foreach (Stats leg in _legs)
            foreach (Stats feet in _feets)
            {
                while (threads.Count >= 2) {}    //im sure thread.join or equivelent can do this instead of a nasty loop :p
    
                var guid = Guid.NewGuid();
                thread worker = new thread(addressof Process, new object() {guid, head, chest, leg, feet, ...});
                worker.start();
                threads.add(guid, worker);
            }
    
        foreach (Thread t in threads)
            t.join();  //this might not be the best as it might make the main thread wait for each thread one after the other, not when all finished.  A manual/auto reset is probably better here.
    }
    
    private void Process(params...)
    {
        int stamina = head.sta + chest.sta + leg.sta + feet.sta;
        int power = head.power + chest.power + leg.power + feet.power;
        int armor = head.armor + chest.armor + leg.armor + feet.armor;
        int hit = head.hit + chest.hit + leg.hit + feet.hit;
    
        double total = stamina * _scaleSta + power * _scalePower + armor * _scaleArmor;
    
        lock _bestscore
        {
            if (total > _bestScore && hit >= 100)
            {
                _bestScore = total;
    
                // Store best setup for output when done with bruteforce
                _bestHead = head;
                _bestChest = chest;
                _bestLeg = leg;
                _bestFeet = feet;
            }
        }
    
        _threads.remove(guid)
    }
    

答案 2 :(得分:0)

你可能会发现添加多个线程实际上会减慢速度。这个计算很慢吗?您对头部,胸部,腿部和脚部的平均预期数量是多少?

你可能最好找到每个头部,胸部等的最高值并丢弃其余部分,找到这些选定部分的最佳组合。

答案 3 :(得分:0)

这个问题的一个方法是取消一些循环,因此创建一些可以并行处理的工作项。像这样:

private struct Stat
{
   int sta;
   int power;
   int armor;
   int hit;
};
private List<Stat[]> workItems = new List<Stat[]>();
private const int NUMBER_OF_CPUS = 4;

private void BruteForce()
{

    //create some work load to split into separate threads
    //we do this by unnesting the first 3 loops.
    //maybe more unnesting is needed to get some more work items
    //for thread to process
    //at least one item per thread/CPU makes sense
    foreach (Stats head in _heads)
      foreach (Stats chest in _chests)
        foreach (Stats leg in _legs)
        {
          this.workItems .Add(new Stat[3] { head, chest, leg });
        }

接下来将把工作项分成可以由单个线程处理的块。您将选择一些等于CPU核心数的线程。你的线程函数至少会有一个参数提供,线程工作项。

线程函数的开头可能如下所示:

private void Process(object param)
        {
            List<Stat[]> threadWorkitems = (List<Stat[]>)param;

            foreach (Stat workItem in threadWorkitems)
            {
                Stats head = threadWorkitems[0];
                Stats chest = threadWorkitems[1];
                Stats leg = threadWorkitems[2];

                foreach (Stats feet in _feets)
                {
                    int stamina = head.sta + chest.sta + leg.sta + feet.sta;
                    int power = head.power + chest.power + leg.power + feet.power;
                    int armor = head.armor + chest.armor + leg.armor + feet.armor;
                    int hit = head.hit + chest.hit + leg.hit + feet.hit;

                    double total = stamina * _scaleSta + power * _scalePower + armor * _scaleArmor;

                    if (total > _bestScore && hit >= 100)
                    {

这段代码中缺少的是,你必须收集所有线程的最佳结果,然后比较它们以找到你真正的最佳设置。

我希望这会给出一些提示。

问候