查询并行 foreach 结果导致 Null 引用错误
Querying parallel foreach result cause Null reference error
我有一个类似下面的并行 foreach 语句
Parallel.ForEach(spacialRecords, (spacerecord) =>
{
List<MeterValue> dat = new List<MeterValue>();
var latitude = Geometry.Y;
var longitude = spacerecord.Geometry.X;
var timeStamp = spacerecord.Timestamp;
foreach (var wdItem in workingData)
{
RepresentationValue spaceMeteredValue = spacerecord.GetMeterValue(wdItem);
if (spaceMeteredValue != null && wdItem.Representation != null)
{
var objMeterValue = new MeterValue();
objMeterValue.key = wdItem.Representation.Code;
objMeterValue.value = spaceMeteredValue.Designator != null ? Convert.ToString(spaceMeteredValue.Designator) : "";
dat.Add(objMeterValue);
}
}
listSpacialRecords.Add(new
{
operationLogDataId = yieldMaster.OperationalLogDataModelResponse.Id,
order = deviceElement.Order,
totalDistanceTravelled = deviceElement.TotalDistanceTravelled,
totalElapsedTime = deviceElement.TotalElapsedTime,
uploadedOn = DateTime.Now.ToUniversalTime(),
collectedOn = timeStamp.ToUniversalTime(),
cropId = "8296e610-c055-11e7-851e-ad7650a5f99c",
productId = productid,
latitude = latitude,
longitude = longitude,
deviceConfigurationId = deviceElement.DeviceConfigurationId,
operationDataId = deviceElement.OperationDataId,
spatialRecords = dat,
depth = depth,
timeStamp = timeStamp,
totaldata = totalRecordCount
});
});
listSpacialRecords 是一个动态类型列表,我在 listSpacialRecords 中获取大量数据。所以我在这里做一些过滤,为此我添加了下面的代码
listSpacialRecords = listSpacialRecords
.Skip(1)
.Aggregate(
listSpacialRecords.Take(1).ToList(),
(a, x) =>
{
if (x.timeStamp.Subtract(a.Last().timeStamp).TotalSeconds >= 10.0)
{
a.Add(x);
}
return a;
});
代码在 foreach 循环之外。当我执行此操作时,出现
之类的错误
Cannot perform run time binding on a null reference
但是当我删除并行并使用普通的 foreach 循环时,代码工作正常。
注意:我做了一个断点,我发现 listSpacialRecords 正确显示了所有记录,我用 quickwatch 检查了最后一个元素数据可用,但仍然失败
谁能帮我弄清楚我的问题是什么?
listSpacialRecords.Add(...);
我假设此方法不是明确线程安全的(默认情况下不会)。所有线程都在与同一个 listSpacialRecords
.
对话
在 Parallel.ForEach
中执行此操作的那一刻,您将面临非常危险的线程竞争 - 如果两个线程同时调用 .Add
, 会出现所有问题可能发生 - 包括数据丢失。
因此:同步所有对此 Add
的调用。这个可以简单到:
var newValue = new {
operationLogDataId = yieldMaster.OperationalLogDataModelResponse.Id,
// etc etc...
}; .
lock(listSpacialRecords) {
listSpacialRecords.Add(newValue);
}
还有一个 dat.Add
,但是这个 dat
是上下文绑定到每个调用的,所以不需要同步。
我有一个类似下面的并行 foreach 语句
Parallel.ForEach(spacialRecords, (spacerecord) =>
{
List<MeterValue> dat = new List<MeterValue>();
var latitude = Geometry.Y;
var longitude = spacerecord.Geometry.X;
var timeStamp = spacerecord.Timestamp;
foreach (var wdItem in workingData)
{
RepresentationValue spaceMeteredValue = spacerecord.GetMeterValue(wdItem);
if (spaceMeteredValue != null && wdItem.Representation != null)
{
var objMeterValue = new MeterValue();
objMeterValue.key = wdItem.Representation.Code;
objMeterValue.value = spaceMeteredValue.Designator != null ? Convert.ToString(spaceMeteredValue.Designator) : "";
dat.Add(objMeterValue);
}
}
listSpacialRecords.Add(new
{
operationLogDataId = yieldMaster.OperationalLogDataModelResponse.Id,
order = deviceElement.Order,
totalDistanceTravelled = deviceElement.TotalDistanceTravelled,
totalElapsedTime = deviceElement.TotalElapsedTime,
uploadedOn = DateTime.Now.ToUniversalTime(),
collectedOn = timeStamp.ToUniversalTime(),
cropId = "8296e610-c055-11e7-851e-ad7650a5f99c",
productId = productid,
latitude = latitude,
longitude = longitude,
deviceConfigurationId = deviceElement.DeviceConfigurationId,
operationDataId = deviceElement.OperationDataId,
spatialRecords = dat,
depth = depth,
timeStamp = timeStamp,
totaldata = totalRecordCount
});
});
listSpacialRecords 是一个动态类型列表,我在 listSpacialRecords 中获取大量数据。所以我在这里做一些过滤,为此我添加了下面的代码
listSpacialRecords = listSpacialRecords
.Skip(1)
.Aggregate(
listSpacialRecords.Take(1).ToList(),
(a, x) =>
{
if (x.timeStamp.Subtract(a.Last().timeStamp).TotalSeconds >= 10.0)
{
a.Add(x);
}
return a;
});
代码在 foreach 循环之外。当我执行此操作时,出现
之类的错误Cannot perform run time binding on a null reference
但是当我删除并行并使用普通的 foreach 循环时,代码工作正常。
注意:我做了一个断点,我发现 listSpacialRecords 正确显示了所有记录,我用 quickwatch 检查了最后一个元素数据可用,但仍然失败
谁能帮我弄清楚我的问题是什么?
listSpacialRecords.Add(...);
我假设此方法不是明确线程安全的(默认情况下不会)。所有线程都在与同一个 listSpacialRecords
.
在 Parallel.ForEach
中执行此操作的那一刻,您将面临非常危险的线程竞争 - 如果两个线程同时调用 .Add
, 会出现所有问题可能发生 - 包括数据丢失。
因此:同步所有对此 Add
的调用。这个可以简单到:
var newValue = new {
operationLogDataId = yieldMaster.OperationalLogDataModelResponse.Id,
// etc etc...
}; .
lock(listSpacialRecords) {
listSpacialRecords.Add(newValue);
}
还有一个 dat.Add
,但是这个 dat
是上下文绑定到每个调用的,所以不需要同步。