C# 异步循环中的Null引用,但对象没有Null
首先,这个问题没有得到回答,因为它描述了简单/基本的对象引用问题。我所经历的与多线程异步处理有关,而另一篇文章并没有解决这个问题 我有一个多线程的.NET winforms应用程序,我正在这样做:C# 异步循环中的Null引用,但对象没有Null,c#,multithreading,thread-safety,C#,Multithreading,Thread Safety,首先,这个问题没有得到回答,因为它描述了简单/基本的对象引用问题。我所经历的与多线程异步处理有关,而另一篇文章并没有解决这个问题 我有一个多线程的.NET winforms应用程序,我正在这样做: if ( paramList != null ) { lock ( paramList ) { foreach ( DictionaryEntry param in paramList ) { command.Parameters.AddWithValue(para
if ( paramList != null ) {
lock ( paramList ) {
foreach ( DictionaryEntry param in paramList ) {
command.Parameters.AddWithValue(param.Key.ToString(), param.Value);
}
}
}
paramList是一个OrderedDictionary
我偶尔会在foreach行上发现这个错误:
对象引用未设置为对象的实例
如您所见,param.Key为null,param.Value为null。但这毫无意义,因为paramList中没有空值,正如您在这里看到的:
在屏幕截图中,您只能看到索引2,但我检查了索引0和1,同样的,也检查了有效数据,没有空值
我没有使用多线程应用程序的经验,但由于中的响应,我将该块锁定。在放入锁之前,我偶尔会发现错误集合被修改了;枚举操作不能执行。在锁定之后,错误消失了,但是现在我得到了如上所示的对象引用
我能做些什么来解决这个问题
编辑
根据几张海报的建议,我这样做了:
private static object syncLock = new object();
然后在用法上向下:
lock ( syncLock ) {
if ( paramList != null ) {
foreach ( DictionaryEntry param in paramList ) {
command.Parameters.AddWithValue(param.Key.ToString(), param.Value);
}
}
}
这似乎解决了对象引用错误,谢谢大家,但现在我偶尔得到:
收集被修改;枚举操作不能执行
因为在尝试这种新方法后,我遇到了一个完全不同的错误。我不确定这样做是否正确,因为现在看来这些问题是相关的,我只是看到同一个核心问题的不同症状
如果有人有想法,仍在寻找解决方案。您的代码将无法工作。如果在null检查期间它不是null,但是在锁定时它是null呢 。对象_mutex=新对象;已经成为习惯 编辑:暗示为私有,但您可能希望按照Charles的建议将其设置为只读 如果锁定变量/字段,可能会遇到此问题。或者原语的装箱没有任何作用,因为原语总是放在不同的盒子里。或者其他人试图锁定调用堆栈,导致死锁 示例代码:
//_mutex is private, readonly and exist exclusively for this operation
lock(_mutex){
//You only do null checks after you got a lock
if ( paramList != null ){
foreach ( DictionaryEntry param in paramList ) {
command.Parameters.AddWithValue(param.Key.ToString(), param.Value);
}
}
}
所有其他读取、写入或处理变量paramList、其任何字典实体及其字段(包括任何副本*)的代码也需要在锁互斥中。所以它们不能相互碰撞
*基本类型和字符串的副本是例外。内置基元类型是不可变的。通常也不要使用参考力学。String类也被设置为inmutable,以允许它以这种方式工作首选更改参数列表,这样就不需要锁定它
如果您需要分享,那么:
锁定不变的对象,例如私有只读对象l=新对象;,或专用静态只读(如果需要)。
检查填充参数列表的代码是否也已锁定。
这是解决我所有问题的解决方案:
var connection = getConnectionFromPool(sourceOrDC);
try {
lock ( connection ) {
using ( SqlCommand command = new SqlCommand(sql, connection) ) {
command.CommandType = CommandType.Text;
if ( paramList != null ) {
foreach ( DictionaryEntry param in paramList ) {
command.Parameters.AddWithValue(param.Key.ToString(), param.Value);
}
}
command.CommandTimeout = 0;
command.ExecuteNonQuery();
}
}
return true;
}
我必须将更多的代码放入锁中,并锁定连接。尝试锁定不同的对象,而不是参数列表查看示例:通常您想要一个:private readonly object myLock=new对象,如果您试图保护的是类变量。否则使锁静止。第二:在AddWithValue之前测试null。第三,您需要在访问参数列表的任何位置使用锁,如果您在此处锁定,但未在添加或删除某些内容的其他位置锁定,则会出现此错误。这是否回答了您的问题?我认为您正处于一个痛苦的世界中,因为您开始使用多线程,但对概念的理解还不够透彻。如果你有时间阅读,这是一个很好的资源:约瑟夫·阿尔巴哈里。我真的很困惑为什么这被否决了。我甚至不是海报!我不明白你的建议。你能根据我发布的代码给出示例代码吗?@Herrmancoder:这是不受欢迎的,我不喜欢复制其他海报作品,但这基本上和我说的一样——使用锁。不过,我确实添加了一些链接和更多解释。所以我觉得没关系,我不明白你的意思。您能根据我发布的代码给出示例代码吗?尝试并运行后,我现在得到的集合已被修改。@HerrimanCoder:Foreach不处理集合,只处理枚举数。转换是隐式完成的,但枚举数有一个规则:如果您重复发送了一个集合,而基础集合发生了更改,则枚举数必须变为无效,并抛出该例外@HerrimanCoder:所以有两个选项:1您没有告诉我们此代码的功能类似于从集合中删除元素2其他一些代码在foreach循环中更改paramList。Wich清楚地说,另一个代码不在lock语句中。您是否忘记了从放入paramList的实例中读取或删除的一些代码?