IsCancellationRequested всегда имеет значение false

#c# #asp.net #model-view-controller #.net-core

#c# #asp.net #модель-представление-контроллер #.net-ядро

Вопрос:

Я вызываю ту же функцию параллельно, используя Task.Whenall().Я И пытаюсь добавить функциональность для остановки выполнения с помощью CancellationTokenSource . CancellationTokenSource возвращает значение true при вызове токена.Cancel() но впоследствии оно изменяется на false и, следовательно, токен.IsCancellationRequested всегда имеет значение false.

     CancellationTokenSource _tokenSource = new CancellationTokenSource();
    [HttpPost]
            public async Task<IActionResult> Runcase(List<string> products,string runnumber,string button)
            {
              var token = _tokenSource.Token;
                try
                {
                    if (ModelState.IsValid)
                    {
                        var productinfo = products;
                        List<string> productids = new List<string>(productinfo);
                        var runnum = runnumber;
                        string runid = "";
                        int count = productids.Count();
                        List<Task> tasks = new List<Task>();
                        int rawid = 0;
                         for (int i = 0; i < count; i  )
                            {
                               tasks.Add(RunServiceAsync(productids[i], runnum,rawid,token));
                            }
                        
                        await Task.WhenAll(tasks);
                        ViewBag.completed = "Success";
                        return View();
                    }
                    else
                    {
                        ViewBag.productinfo = new MultiSelectList(_context.inputValuesConfigurations, "ProductID", "ProductName");
                        ModelState.AddModelError(string.Empty, "Please enter a valid data...");
                        return View();
                    }
                }
                catch(Exception ex)
                {
                    return View();
                }
            }  

Когда я вызываю эту функцию, токен возвращает значение true

  public void Stopexecution()
        {
            _tokenSource.Cancel();
        }  

Но в приведенном ниже коде это всегда false

 public async Task RunServiceAsync(string productids,string runnumber,int rawid,CancellationToken token)
        {
            using(var dbscope = _service.CreateScope())
            {
                var dbcontext = dbscope.ServiceProvider.GetRequiredService<AppDbContext>();
                var productid = Convert.ToInt32(productids);
                var inputvals = dbcontext.inputValuesConfigurations.FirstOrDefault(m => m.ProductID == productid);
                var run = runnumber   '_'   inputvals.ProductID;
                int count = 0;
                bool completion1 = false; // for j
                int totalCount = CountProductvalues(inputvals);
                while (!completion1) // 
                {
                    var errorrun = dbcontext.errorlogs.Select(m => m.ProductID == productid).ToList().Count();
                    totalCount = completion == true ? errorrun : totalCount;
                    for (int j = rawid; j < totalCount; j  )
                    {
                        if(token.IsCancellationRequested)
                        {
                            token.ThrowIfCancellationRequested();
                        }
                        if(!completion) // Stop if First run complete
                        {
                            Inputvalues c1 = new Inputvalues(j, inputvals);
                            InputValuesViewmodel inputValues = new InputValuesViewmodel()
                            {
                                ProductID = productid,
                                STATE = c1.State,
                                AGE1 = c1.AGE1,
                                SEX1 = c1.SEX1,
                                UND_CLASS1 = c1.UND_CLASS1,
                                FACE_OPTIONS = 1,
                                FACE_SCHEDULE = "1ü"   c1.SOLVE_TARGET_YEAR   "0ü"   c1.FACE_SCHEDULE,
                                PREM_OPTIONS = premoption,
                                PAY_PREM_TARGET_ID = c1.PAY_PREM_TARGET_ID,
                                PAY_PREM_TARGET_YEAR = c1.PAY_PREM_TARGET_YEAR,
                                SOLVE_TARGET_ID = 1,
                                SOLVE_TARGET_YEAR = c1.SOLVE_TARGET_YEAR
                            };
                            await RecieveResponseasync(inputValues, run, j,productid);
                            
                            //completion = j == totalCount ? true : false;
                        }
                    }
                };
            }
        }  

Вызов Stopexecution()

 $("#btnstop").click(function (e) {
                    
                    $("#btnstart span").text("Rerun");
                    let btnstartval = $("#btnstart").val('Rerun');
                    e.preventDefault();
                    $.ajax({
                        url: "@Url.Action("Stopexecution", "CallService")",
                        type: "POST",
                        dataType: "json",
                    });
                });
            });  

Комментарии:

1. Куда вы звоните StopExecution() ?

2. Пожалуйста, покажите, где вы берете токен из _tokenSource и передаете его методу.

3. Я только что обновил свой код. Не могли бы вы взглянуть на это еще раз? Спасибо

4. @insane_developer Я вызываю StopExecution() из представления, используя вызов ajax.

5. @Neu я думаю, что одним из способов было бы сделать его статичным, но тогда вы должны посмотреть, влияет ли это на другие вызовы, которые могут использовать токен. В любом случае, это, вероятно, не очень хороший подход, контроллер со статическим свойством. Почему вы должны отменить токен, основываясь на чем?

Ответ №1:

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

Чтобы исправить это, вы могли бы сделать это статическим:

 static CancellationTokenSource _tokenSource = new CancellationTokenSource();
  

Не забывайте сбрасывать этот источник каждый раз, когда выполняется запрос … потому что, как только он отменяется, он отменяется навсегда. Кроме того, что произойдет, если эта конечная точка будет вызвана два или более раз одновременно. У вас будет состояние гонки.

Вторая проблема: это не очень «подобный контроллеру» шаблон. Вам следует переосмыслить, как вы генерируете данные. Если вызов конечной точки занимает больше 100 мс, то вы столкнетесь с проблемами нехватки потоков / сокетов, если ваша служба перегреется.

Комментарии:

1. Спасибо @Andy. Это сработало, и я рассмотрю рекомендованный вами подход.

2. @Neu — нет проблем… Надеюсь, вы сможете разобраться с этим, чтобы вам не нужно было отменять ожидающий запрос 🙂 Удачи вам.