Отменить параллельный процесс

У меня есть MainProcess.cs и ChildProcess.cs. Основной процесс используется для сбора всей необходимой информации и отправки всех данных в параллельный цикл foreach. Дочерний процесс будет вызывать отдельный API.

MainProcess.cs

//MainProcess.cs

string resourceId = GenerateUID();
Parallel.ForEach(Accounts, parallelOptions, async (account) =>
{                
    await childProcess.ProcessMethod(resourceId, account);

});

ChildProcess.cs

//ChildProcess.cs

public async Task ProcessMethod(string resourceId, string account){

    var Req = {}; //Required info set in here.

    using (HttpClient client = new HttpClient())
    {

        var result = await client.PostAsJsonAsync($"{baseUrl}api/services/app/Process/Run", Req);
        if (result.IsSuccessStatusCode)
        {
            //Save returned data to DB against the resourceId. (Figure 1)
        }
    }
}

Данные сохраняют таблицу, как показано ниже.

Рисунок 1

введите здесь описание изображения

Существует класс MainProcess, и у него есть метод для параллельного запуска задач. Есть класс ChildProcess и у него есть ProcessMethod. Внутри ProcessMethod я вызываю отдельный API для выполнения некоторой работы. Вышеупомянутый код и процедура работают на 100% правильно.

Но моя проблема в том, что в какой-то момент мне нужно отменить обработку. Давайте воспользуемся приведенным ниже рисунком 2, чтобы упростить понимание.

Рисунок 2

введите здесь описание изображения

Согласно приведенному выше рисунку, общий процесс завершится примерно через 10 секунд. На 4-й секунде мне нужно отменить обработку. (Вызов 1 и Вызов 5 уже вернули ответ) Вызов 2, Вызов 3, Вызов 4 отправили запрос, но еще не получили ответ.

Что мне нужно сделать, это отменить все процессы в данный момент времени. Просто это похоже на то, что я набрал URL-адрес в браузере и нажал Enter. перед появлением веб-страницы я закрываю браузер. Так что мне наплевать на ответ.

Я пытаюсь сделать это с токеном отмены. Но где я застрял, я не мог отменить вызовы API для определенного идентификатора ресурса.

Затем я изменил код ChildProcess.cs.

добавлена ​​переменная экземпляра

CancellationTokenSource tokenSource = new CancellationTokenSource();

затем изменить

var result = await client.PostAsJsonAsync($"{baseUrl}api/services/app/Process/Run", Req, tokenSource.Token);

Я знаю, что он должен использовать приведенный ниже код для отмены процесса.

tokenSource.Cancel();

Но проблема в том, как отменить конкретный процесс resourceId из другой функции? (Отменить процесс с определенным идентификатором ресурса, потому что ProcessMethod можно вызывать одновременно. Если он обрабатывает процесс со вторым идентификатором ресурса, это не должно влиять на второй процесс. Пакет с 1-м идентификатором ресурса должен быть закрыт/отменен)

Любой эксперт может помочь мне в этом?


comment
Parallel.ForEach не поддерживает асинхронность.   -  person Theodor Zoulias    schedule 23.11.2020


Ответы (1)


Вышеупомянутый код и процедура работают на 100% правильно.

Это действительно не так. async нельзя использовать с Parallel. Вместо параллелизма (несколько потоков) следует использовать асинхронный параллелизм (несколько операций без потока для каждого):

//MainProcess.cs
string resourceId = GenerateUID();
var tasks = Accounts.Select(account => childProcess.ProcessMethod(resourceId, account)).ToList();
await Task.WhenAll(tasks);

Но проблема в том, как отменить конкретный процесс resourceId из другой функции? (Отменить процесс с определенным идентификатором ресурса, потому что ProcessMethod можно вызывать одновременно. Если он обрабатывает процесс со вторым идентификатором ресурса, это не должно влиять на второй процесс. Пакет с 1-м идентификатором ресурса должен быть закрыт/отменен)

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

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

Что-то вроде этого:

public async Task ProcessMethod(string resourceId, string account)
{
  try
  {
    var Req = {}; //Required info set in here.
    using (HttpClient client = new HttpClient())
    {
      var result = await client.PostAsJsonAsync($"{baseUrl}api/services/app/Process/Run", Req, tokenSource.Token);
      if (result.IsSuccessStatusCode)
      {
        //Save returned data to DB against the resourceId. (Figure 1)
      }
    }
  }
  catch (OperationCanceledException)
  {
  }
}
person Stephen Cleary    schedule 23.11.2020
comment
Большое спасибо за ответ. Извинения за то, что не дал быстрый ответ на ваш достойный ответ. Я немного изменил ваш код. Это потому, что resourceId должен вернуться до завершения всей работы. var tasks = Accounts.Select(async(account) =› await childProcess.ProcessMethod(resourceId, account)).ToList(); Задача.КогдаВсе(задачи.ToArray()); Затем я делаю Thread.Sleep(1000); а затем вызовите cancelProcess(); Его реализации: public void cancelProcess() { tokenSource.Cancel(); } Согласно коду, он не должен возвращать значения после выполнения 1-й секунды. Но все еще выполняются задания в фоновом режиме. - person weeraa; 26.11.2020