Известные побочные эффекты при использовании оператора блокировки в ConcurrentDictionary.GetOrAdd?C#

Место общения программистов C#
Ответить Пред. темаСлед. тема
Гость
 Известные побочные эффекты при использовании оператора блокировки в ConcurrentDictionary.GetOrAdd?

Сообщение Гость »

У меня возникли некоторые проблемы при добавлении в Entity Framework DbSet из нескольких потоков из метода ConcurrentDictionary ValueFactory. Я попытался устранить эту проблему, введя оператор блокировки. Однако, похоже, это имеет некоторые странные побочные эффекты. В некоторых редких и случайных случаях мой код выдает исключение KeyNotFoundException, хотя программа должна предотвратить это. Наверное, я что-то контролирую.

Код: Выделить всё

using (ESBClient client = new ESBClient()) { // WCF SERVICE

client.Open();

// Limit the maximum number of parallel requests
var esbLimiter = new SemaphoreSlim(4);

ConcurrentDictionary dataEntryDict  = new ConcurrentDictionary(
await db.DataEntries
.Where(de => allObjIDs.Contains(de.PAObjID))
.IncludeOptimized(de => de.WorkSchedules)
.ToDictionaryAsync(a => a.PAObjID, a => a)
);

// Get WorkOrderDataSet02 for each data entry number
await Task.WhenAll(allDataEntryNumbers.Batch(20).Select(async workOrderBatch => {
await esbLimiter.WaitAsync();

Debug.WriteLine($"Starting for new batch after {s.ElapsedMilliseconds} with parallel {esbLimiter.CurrentCount}");

try {
int retryCounter = 0;
getWorkOrderDataSet02Response gwoResp;

retryCurrentWorkOrderDataSetResp:
try {
gwoResp = await client.getWorkOrderDataSet02Async(
new getWorkOrderDataSet02Request(
"?",
companyGroup.Key,
string.Join(",", workOrderBatch.Select(wob => wob.DataEntryNumber)),
"WNTREIB",
"?",
"act,sales",
"D"
)
);

} catch (System.ServiceModel.CommunicationException ex) {
// Retry up to 3 times before finally crashing
if (retryCounter++ < 3) {
await HandleServiceRetryError("getWorkOrderDataSet02Async", retryCounter, s.ElapsedMilliseconds, ex);
goto retryCurrentWorkOrderDataSetResp;
} else
throw;
}

// Iterate over all work orders returned by the ESB
foreach (dsyWorkOrder01TtyWorkOrder currDetail in gwoResp.dsyWorkOrder01) { // dsyWorkOrder01 IS AN ARRAY OF OBJECTS. IT COMES FROM A WCF CALL.  PAObjID IS UNIQUE.
// Get or create element
DataEntry currentEntry = dataEntryDict.GetOrAdd(
currDetail.Obj,
key => {
DataEntry newDe = new DataEntry();
lock (db.DataEntries) { // I INTRODUCED THOSE LOCK STATEMENTS
db.DataEntries.Add(newDe); // THIS IS THE LINE THAT WAS PROBLEMATIC IN THE FIRST PLACE
}
return newDe;
}
);

// Set regular fields
currentEntry.ApplyTtyWorkOrder(currDetail, resourceDict); // THIS METHOD APPLIES THE PAObjID PROPERTY
}

// Delete all elements, that were not provided by the service anymore
lock(db.DataEntries) {
workOrderBatch
.Where(wob => !gwoResp.dsyWorkOrder01
.Where(wo => wo.DataEntryNumber.HasValue)
.Select(wo => wo.DataEntryNumber.Value)
.Contains(wob.DataEntryNumber)
)
.ToArray()
.ForEach(dataEntry => {
try {
db.DataEntries.Remove(dataEntryDict[dataEntry.ObjID]); // THIS LINE THROWS THE KeyNotFoundException
} catch (Exception ex) {
throw new Exception($"Key {dataEntry.ObjID} not in list.", ex);
}

});
}

// Update progress
progress.Report(.1f + totalSteps * Interlocked.Increment(ref currentStep) * .8f);

} finally {
Debug.WriteLine($"Finished for batch after {s.ElapsedMilliseconds} with parallel {esbLimiter.CurrentCount}");
esbLimiter.Release();
}

}));
}

// HERE'S THE APPLY METHOD
public void ApplyTtyWorkOrder(dsyWorkOrder01TtyWorkOrder src, Dictionary resourceDict) {
Deleted = false;

DataEntryNumber = src.DataEntryNumber.Value;
PAObjID = src.Obj; // PAObjID IS APPLIED HERE
IsHeader = src.IsHeader;
Pieces = Convert.ToInt16(src.ProductionQty);
PartNo = src.Article;
JobNo = src.WorkOrder;
StartDate = src.StartDate;
FinishDate = src.EndDate;
FinishedPA = src.WorkOrderStatus == "R";

// Update methods
UpdateFromTtyCustomer(src.ttyCustomer?.FirstOrDefault());
UpdateFromPart(src.ttyPart?.FirstOrDefault());
UpdateFromSalesDocHeader(src.ttySalesDocHeader?.FirstOrDefault());
UpdateWorkSchedules(src.ttyWorkOrderActivity, resourceDict);
}
Я добавил комментарий ПРОПИСНЫМИ буквами к каждой строке, которую считаю релевантной.

Я понятия не имею, почему возникла эта ошибка. случается. Насколько я понимаю, я пытаюсь получить запись только из ключей словаря dataEntryDict dataEntry.ObjID, которые я добавил ранее в той же итерации цикла.
До того, как я представил два оператора блокировки, строка, помеченная как «ЭТА СТРОКА, КОТОРАЯ БЫЛА ПРОБЛЕМНОЙ В ПЕРВУЮ ОЧЕРЕДЬ», время от времени вызывала исключение: «Коллекция была изменена; операция перечисления может не выполниться». Покопавшись в коде EF, я понял, что это должно быть как-то связано с тем, как реализован метод DbSet.Add.

Есть ли там какие-либо известные побочные эффекты при использовании оператора блокировки внутри ValueFactory?

Подробнее здесь: https://stackoverflow.com/questions/607 ... y-getoradd
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение
  • Гарантируется ли ConcurrentDictionary.GetOrAdd() вызов valueFactoryMethod только один раз для каждого ключа?
    Anonymous » » в форуме C#
    0 Ответы
    26 Просмотры
    Последнее сообщение Anonymous
  • Как я могу сказать ConcurrentDictionary.GetOrAdd не добавлять значение?
    Anonymous » » в форуме C#
    0 Ответы
    12 Просмотры
    Последнее сообщение Anonymous
  • Как я могу сказать ConcurrentDictionary.GetOrAdd не добавлять значение?
    Гость » » в форуме C#
    0 Ответы
    8 Просмотры
    Последнее сообщение Гость
  • CONCURRENTDICTIONARY PHIDEFLE - Являются ли делегатные фабрики из Getoradd и AddorUpdate Synchronized?
    Anonymous » » в форуме C#
    0 Ответы
    18 Просмотры
    Последнее сообщение Anonymous
  • Concurrentdictionary getoradd метод valueFactory выполнение [дубликат]
    Anonymous » » в форуме C#
    0 Ответы
    1 Просмотры
    Последнее сообщение Anonymous

Вернуться в «C#»