Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/323.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用lock()的C#事件侦听器仍然抛出异常_C#_List_Events_Exception_Xna - Fatal编程技术网

使用lock()的C#事件侦听器仍然抛出异常

使用lock()的C#事件侦听器仍然抛出异常,c#,list,events,exception,xna,C#,List,Events,Exception,Xna,我正在制作一个xna游戏,问题涉及以下几类: 游戏类-该类侦听下面两个类的事件侦听器 玩家类-该类启动Fire()事件,告诉游戏玩家发射子弹 Bullet类-该类启动一个SelfDestruct()事件(经过一定的移动距离后),告诉游戏必须删除实例 游戏类有一个项目符号列表,在更新方法中,它在此列表上执行foreach操作 Fire()的事件侦听器将新项目符号添加到项目符号列表中 SelfDestruct()的事件侦听器(通过强制转换为项目符号)从列表中删除发送者 这两个事件以及更新方法都会锁定

我正在制作一个xna游戏,问题涉及以下几类:
游戏类-该类侦听下面两个类的事件侦听器
玩家类-该类启动Fire()事件,告诉游戏玩家发射子弹
Bullet类-该类启动一个SelfDestruct()事件(经过一定的移动距离后),告诉游戏必须删除实例

游戏类有一个项目符号列表,在更新方法中,它在此列表上执行foreach操作
Fire()的事件侦听器将新项目符号添加到项目符号列表中 SelfDestruct()的事件侦听器(通过强制转换为项目符号)从列表中删除发送者

这两个事件以及更新方法都会锁定列表以确保线程安全。 但是它仍然抛出一个异常,告知列表在foreach期间被修改

我如何解决这个问题;因为我确实锁定了名单。。但这是行不通的:

Update:
public void Update(GameTime gameTime)
{
    player.Update(GameTime gameTime);//can throw fire event
    lock(Bullets)//Lock the list for thread safett
    {
        foreach(Bullet b in Bullets)//Throws exception when bullet is added/removed
            b.Update(gameTime);//can throw selfdestruct event
    }
}

Fire listener:
void listen_fire(object sender, EventArgs e)
{
    Player p = (Player)sender;/used to get coordinates and rotation stored in the player
    lock(Bullets)
    {
        Bullets.Add(new Bullet(p.Position,p.Rotation));
    }
}

Self destruct listener:
void listen_selfdestruct(object sender, EventArgs e)
{
    lock(Bullets)
    {
        Bullets.Remove((Bullet)sender);
    }
}
我认为这个解决方案可能会失败,因为事件被抛出到一个已经锁定了列表的线程中


欢迎提供任何解决方案,感谢您阅读我的问题

foreach中使用的集合是不可变的。这是精心设计的

正如上面所说:

foreach语句用于 遍历集合以获取 你想要的信息,但可以 不能用于添加或删除项目 从源集合中删除以避免 不可预测的副作用如果你 需要从中添加或删除项目 源集合,使用for循环。

例如,此代码将引发异常:

     List<string> lst = new List<string>();

     lst.Add("aaa");
     lst.Add("bbb");

     foreach (string curr in lst)
     {
        if (curr.Equals("aaa"))
        {
           lst.Remove(curr);
        }
     }
List lst=new List();
第1条添加(“aaa”);
第1条添加(“bbb”);
foreach(lst中的字符串curr)
{
如果(当前等于(“aaa”))
{
第一次移除(当前);
}
}
因此,我这样做是为了在迭代时不会从列表中删除:

     List<string> lst = new List<string>();

     lst.Add("aaa");
     lst.Add("bbb");
     List<string> lstToDel = new List<string>();

     foreach (string curr in lst)
     {
        if (curr.Equals("aaa"))
        {
           lstToDel.Add(curr);
        }
     }

     foreach (string currToDel in lstToDel)
     {
        lst.Remove(currToDel);
     }
List lst=new List();
第1条添加(“aaa”);
第1条添加(“bbb”);
List lstToDel=新列表();
foreach(lst中的字符串curr)
{
如果(当前等于(“aaa”))
{
lstToDel.Add(货币);
}
}
foreach(lstToDel中的字符串currToDel)
{
第一次移除(currToDel);
}

现在我不知道您的
项目符号中有哪些项目
,但在
foreach
语句中,您无法更新或删除它们

当同一线程上的附加
lock
语句已提升到该线程的
lock
时,将忽略它们。您的
foreach
最初会锁定该线程的代码,阻止所有其他线程,但该代码可能会触发一个事件(非异步),该事件也会锁定。。。但是,由于事件与foreach位于同一个线程上,因此代码可以在事件中输入锁块,并且您试图从正在枚举的列表中删除,就好像您从未锁定过一样

这种设计的原因是一个线程一次可以做一件事。。。因此,如果线程已经拥有锁,那么重新输入锁是可以的,因为它不能同时位于两个位置。。。因此,对于当前可以访问锁的单线程来说,线程安全问题的风险并不存在——它不能像单线程应用程序的线程安全那样与自身竞争


为了避免这一点,考虑使用线程安全的集合,或将工作委派给工作线程/任务。

谢谢,我给了它更多的思考,并且只有从列表中添加或移除项的事件会引起问题,所以我会给实体一些更新调用之后检查的属性(仍然在前述循环中)。将要删除/添加的项目添加到单独的列表中,然后枚举这些项目