XNA中的后台线程失败,“收集已更改”。

时间:2014-12-18 17:16:48

标签: c# multithreading xna

我试图在XNA 4,C#上创建类似粒子系统的东西。

我已经创建了一个函数,如果粒子足够接近,它们会让粒子彼此移动。它包含循环周期,因此它是滞后的。没有这个功能,程序开始滞后400-500颗粒,功能 - 接近180。

问题1.我可以通过创建后台线程来处理这些粒子碰撞来改善性能吗?

因此,创建了内部计时器工作的线程。当我启动我的游戏时,它开始正常,但当粒子数量超过接近130-150时,会出现运行时错误Collection has changed。无法执行枚举。" (它是来自另一种语言的翻译,而不是确切的消息)。 收集"邻居" - 函数Particle.RunAwayFromNeighbours中的局部变量 - 在枚举过程中被更改,然后,我认为,一个线程以某种方式调用函数,而前一个调用未完全执行。一件奇怪的事。

我尝试使用Threading.Monitor类来同步调用,但我对多线程编程的经验很少,所以我觉得我做错了。它没有解决问题,但是在相同的粒子数下它仍然是同样的错误。

我也尝试使用lock运营商,但情况仍然如此。

问题2.如何最终同步线程?

有班主任,他"拥有"一些粒子。其他线程在此类中有效。下面的代码是使用Monitor类编写的。 代码(......不是必需的部分):

class Player
    {
       ...
        Thread CollisionThread; 
        static double check_period=100;
        System.Timers.Timer checktimer = new System.Timers.Timer(check_period);
        public void StartCollisionChecking()
        {
            checktimer.AutoReset = true;
            checktimer.Elapsed += (o, e) => { CheckCollisions(); };
            CollisionThread = new Thread(this.CheckCollisionCycle);
            CollisionThread.Start();
        } //call in LoadContent
        void CheckCollisionCycle() 
        {
            checktimer.Start();
        }  //call 1 time in new thread
        void CheckCollisions()
        {
            for (int i = 0; i < Army.Count - 1; i+=2 )
            {
                var p = Army[i];
                p.RunAwayFromNeighbours();
            }
        }  //called in CheckCollisionCycle
}
class Particle : VO
    {
static float neighbour_search_radius = 2;
        static float run_speed_q = 0.1f;
        IEnumerable<Particle> CheckForNeighbours()
        {
            return owner.GetArmy().Where(a => Vector2.Distance(a.location.GetXY(), location.GetXY()) < neighbour_search_radius);
        }
        public void RunAwayFromNeighbours()
        {
            object x = new object() ;
            Monitor.Enter(x);
            try
            {
                var neighbours = CheckForNeighbours();
                foreach (Particle p in neighbours)
                {
                    Vector2 where_to_run = location.GetXY() - p.location.GetXY();
                    speed += where_to_run * run_speed_q;
                }
            }
            finally
            {
                Monitor.Exit(x);
            }


        }

2 个答案:

答案 0 :(得分:0)

有可能neighbours期间正在修改foreach,请尝试:

foreach (Particle p in neighbours.toList())

看看是否对其进行排序。

这将让您迭代neighbours的副本。它不是最佳解决方案(你应该首先避免这种冲突),但它是一个快速的解决方法,看看它是否真正存在于故障所在。< / p>

答案 1 :(得分:0)

我通过做两件事解决了这个问题: 我在类Particle中添加了bool变量,它显示了线程是否完成了它的工作。它看起来像这样:

public bool thread_completed = true;
        public void RunAwayFromNeighbours()
        {
            thread_completed = false;
            object x = new object() ;
            Monitor.Enter(x);
            try
            {
                var neighbours = CheckForNeighbours().ToList();
                foreach (Particle p in neighbours)
                {
                    Vector2 where_to_run = location.GetXY() - p.location.GetXY();
                    speed += where_to_run * run_speed_q;

                }
            }
            finally
            {

                Monitor.Exit(x);
                thread_completed = true;
            }

2。我换线了 return owner.GetArmy().Where(a => Vector2.Distance(a.location.GetXY(), location.GetXY()) < neighbour_search_radius).ToList();

return owner.GetArmy().ToList().Where(a => Vector2.Distance(a.location.GetXY(), location.GetXY()) < neighbour_search_radius);