Arunprasanth K V
Arunprasanth K V

Reputation: 21931

Querying parallel foreach result cause Null reference error

I have a parallel foreach statement like below

  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 is a dynamic type list and i am getting a huge number of data in listSpacialRecords. So here i am doing some filtering, for that i have added the below code

 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;
                      });

The code is outside of foreach loop. And when i execute this I am getting error like

Cannot perform run time binding on a null reference

But when i remove the parallel and use normal foreach loop the code works fine.

Note : I made a break point over and I found listSpacialRecords shows all the records properly, I have checked with quickwatch and upto last element data is available but still it fails

Can anyone help me to figure out what i the issue?

Upvotes: 3

Views: 2994

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1062600

listSpacialRecords.Add(...);

I'm assuming that this method is not explicitly thread-safe (it won't be by default). All the threads are talking to the same listSpacialRecords.

The moment you do this inside Parallel.ForEach, you have a very risky thread race - if two threads are calling .Add at the same time, all number of bad things can happen - including data loss.

So: synchronize all calls to this Add. This could be as a simple as:

var newValue = new {
    operationLogDataId = yieldMaster.OperationalLogDataModelResponse.Id,
    // etc etc...
};                           .
lock(listSpacialRecords) {
    listSpacialRecords.Add(newValue);
}

There's also a dat.Add, but this dat is context-bound to each call, so shouldn't need synchronization.

Upvotes: 8

Related Questions