使用lock()的C#事件侦听器仍然抛出异常
我正在制作一个xna游戏,问题涉及以下几类:使用lock()的C#事件侦听器仍然抛出异常,c#,list,events,exception,xna,C#,List,Events,Exception,Xna,我正在制作一个xna游戏,问题涉及以下几类: 游戏类-该类侦听下面两个类的事件侦听器 玩家类-该类启动Fire()事件,告诉游戏玩家发射子弹 Bullet类-该类启动一个SelfDestruct()事件(经过一定的移动距离后),告诉游戏必须删除实例 游戏类有一个项目符号列表,在更新方法中,它在此列表上执行foreach操作 Fire()的事件侦听器将新项目符号添加到项目符号列表中 SelfDestruct()的事件侦听器(通过强制转换为项目符号)从列表中删除发送者 这两个事件以及更新方法都会锁定
游戏类-该类侦听下面两个类的事件侦听器
玩家类-该类启动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位于同一个线程上,因此代码可以在事件中输入锁块,并且您试图从正在枚举的列表中删除,就好像您从未锁定过一样
这种设计的原因是一个线程一次可以做一件事。。。因此,如果线程已经拥有锁,那么重新输入锁是可以的,因为它不能同时位于两个位置。。。因此,对于当前可以访问锁的单线程来说,线程安全问题的风险并不存在——它不能像单线程应用程序的线程安全那样与自身竞争
为了避免这一点,考虑使用线程安全的集合,或将工作委派给工作线程/任务。
谢谢,我给了它更多的思考,并且只有从列表中添加或移除项的事件会引起问题,所以我会给实体一些更新调用之后检查的属性(仍然在前述循环中)。将要删除/添加的项目添加到单独的列表中,然后枚举这些项目