Цикл Parallel.ForEach Разрешить только одному потоку доступ к БД в C # - PullRequest
0 голосов
/ 21 июня 2019

У меня есть фрагмент кода, к которому несколько потоков обращаются с использованием свойства общего идентификатора из строки типа ConcurrentBag, например:

var ids = new ConcurrentBag<string>();
// List contains lets say 10 ID's 
var apiKey = ctx.ApiKey.FirstOrDefault();

Parallel.ForEach(ids, id => 
{
    try
    {
        // Perform API calls
    }
    catch (Exception ex)
    {
        if (ex.Message == "Expired")
        {
            // the idea is that if that only one thread can access the DB record to update it, not multiple ones
            using (var ctx = new MyEntities())
            {
                var findApi= ctx.ApiKeys.Find(apiKey.RecordId);
                findApi.Expired = DateTime.Now.AddHours(1);
                findApi.FailedCalls += 1;
            }
        }
    }

});

Так что в такой ситуации, если у меня есть список из 10 идентификаторови 1 ключ, который используется для вызова API, как только ключ достигает почасового ограничения вызовов, я поймаю исключение из API и затем отметлю ключ, который не будет использоваться в течение следующего часа.

Однако,в коде, который я вставил выше, все 10 потоков получат доступ к записи из БД и будут считать неудавшиеся вызовы как 10 раз, а не только 1 ..: /

Так что мой вопрос здесь заключается в том, как мнезапретить всем потокам выполнять обновление записи БД, но вместо этого разрешить доступ к БД только одному потоку, обновить запись (добавить неудачные вызовы +1)?

Как мне этого добиться?

Ответы [ 2 ]

2 голосов
/ 21 июня 2019

Похоже, вам нужно обновить apiKey.RecordId только один раз, если произошла ошибка, почему бы просто не отследить факт возникновения ошибки и обновить один раз в конце? например,

var ids = new ConcurrentBag<string>();
// List contains lets say 10 ID's 
var apiKey = ctx.ApiKey.FirstOrDefault();
var expired = false;

Parallel.ForEach(ids, id => 
{
    try
    {
        // Perform API calls
    }
    catch (Exception ex)
    {
        if (ex.Message == "Expired")
        {
           expired = true;
        }
    }
}

if (expired)
{
   // the idea is that if that only one thread can access the DB record to 
   // update it, not multiple ones
   using (var ctx = new MyEntities())
   {
     var findApi= ctx.ApiKeys.Find(apiKey.RecordId);
     findApi.Expired = DateTime.Now.AddHours(1);
     findApi.FailedCalls += 1;
    }
});
1 голос
/ 21 июня 2019

Вы находитесь в параллельном цикле, поэтому наиболее вероятным поведением является то, что каждый из 10 потоков будет запускаться, попытаться подключиться к вашему API с ключом с истекшим сроком действия, а затем все завершится неудачно, выдав исключение.

Существует несколько разумных решений:

Проверьте ключ перед его использованием

Может ли первый цикл цикла пройти?например:

var ids = new ConcurrentBag<string>();
var apiKey = ctx.ApiKey.FirstOrDefault();

bool expired = true;

try {
  // Perform API calls
  expired = false;
}
catch(Exception ex) {
   // log to database once
}

// Or grab another, newer key?
if (!expired)
{
  Parallel.ForEach(ids.Skip(1), id => 
  {
     // Perform API Calls
  }
}

Это будет работать вполне разумно, если, вероятно, срок действия ключа истек до его использования, но будет активным во время его использования.

Сохраните ошибки

Если ключ, возможно, действителен при запуске, но срок его действия может истечь во время его использования, вы можете попытаться перехватить этот сбой и затем зарегистрировать его в конце.

var ids = new ConcurrentBag<string>();
var apiKey = ctx.ApiKey.FirstOrDefault();

// Assume the key hasn't expired - don't set to false within the loops 
bool expired = false;

Parallel.ForEach(ids.Skip(1), id => 
{
  try {
     // Perform API calls
  }
  catch (Exception e) {
    if (e.Message == "Expired") {
      // Doesn't matter if many threads set this to true.
      expired = true;
    }
  }

  if (expired) {
    // Log to database once.
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...