取消并行进程

Posted

技术标签:

【中文标题】取消并行进程【英文标题】:Cancel parallel process 【发布时间】:2021-03-06 06:30:49 【问题描述】:

我有一个 MainProcess.cs 和 ChildProcess.cs。 Mainprocess 用于收集所有必需的信息并将所有数据提交到并行 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($"baseUrlapi/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 并回车。在出现网页之前,我关闭了浏览器。所以我不在乎回复。

我尝试使用取消令牌来执行此操作。但我卡住的地方是,我无法取消对特定 resourceId 的 api 调用。

然后我修改了 ChildProcess.cs 的代码。

添加了实例变量

CancellationTokenSource tokenSource = new CancellationTokenSource();

然后改变

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

我知道,取消过程应该使用下面的代码。

tokenSource.Cancel();

但问题是如何从不同的函数中取消特定resourceId的进程? (取消特定resourceId的进程,因为ProcessMethod可以并发调用。如果是在处理2nd resourceId的进程,应该不会影响到2nd进程。第1个resourceId批次应该关闭/取消)

哪位专家可以帮助我解决这个问题?

【问题讨论】:

Parallel.ForEachis not async-friendly. 【参考方案1】:

上述代码和程序100% 正确运行。

真的不是。 async 不能与 Parallel 一起使用。而不是并行(多线程),你应该使用异步并发(多个操作没有每个线程):

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

但问题是如何从不同的函数中取消特定resourceId的进程? (取消特定resourceId的进程,因为ProcessMethod可以并发调用。如果是处理2nd resourceId的进程,应该不会影响到2nd进程。第1个resourceId批次应该关闭/取消)

您无需为此执行任何特殊逻辑。您可以对所有这些操作使用相同的取消令牌,因为每个操作在完成后都会忽略取消请求。

您需要添加的唯一代码是忽略任何取消异常。

类似这样的:

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($"baseUrlapi/services/app/Process/Run", Req, tokenSource.Token);
      if (result.IsSuccessStatusCode)
      
        //Save returned data to DB against the resourceId. (Figure 1)
      
    
  
  catch (OperationCanceledException)
  
  

【讨论】:

非常感谢您给我答复。很抱歉没有对您有价值的答案做出快速反应。我稍微修改了你的代码。这是因为 resourceId 应该在完成整个工作之前返回。 var tasks = Accounts.Select(async(account) => await childProcess.ProcessMethod(resourceId, account)).ToList(); Task.WhenAll(tasks.ToArray());然后我做 Thread.Sleep(1000);然后调用cancelProcess();它的实现是, public void cancelProcess() tokenSource.Cancel();根据代码,它不应该在执行第一秒后返回值。但仍在后台运行作业。

以上是关于取消并行进程的主要内容,如果未能解决你的问题,请参考以下文章

并行取消嵌套多个数组

多进程取消链接共享内存,需要只有在所有进程退出时才应该取消链接共享内存

即使使用取消方法也无法取消 AsyncTask 中的 doInBackground 进程

取消 handler.postdelayed 进程

如何通过子进程取消警报()信号?

MS Access - 通过取消报告创建的“幻影”进程