0

我有一个 MainProcess.cs 和 ChildProcess.cs。Mainprocess 用于收集所有需要的信息并将所有数据提交到并行 foreach 循环。子进程将调用单独的 API。

主进程.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 并回车。在出现网页之前,我关闭了浏览器。所以我不在乎回复。

我尝试使用取消令牌来做到这一点。但是我坚持的地方是,我无法取消对特定资源 ID 的 api 调用。

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

添加了实例变量

CancellationTokenSource tokenSource = new CancellationTokenSource();

然后改变

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

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

tokenSource.Cancel();

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

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

4

1 回答 1

3

上面提到的代码和程序是 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。如果是处理第二个resourceId的进程,应该不会影响到第二个进程。第一个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($"{baseUrl}api/services/app/Process/Run", Req, tokenSource.Token);
      if (result.IsSuccessStatusCode)
      {
        //Save returned data to DB against the resourceId. (Figure 1)
      }
    }
  }
  catch (OperationCanceledException)
  {
  }
}
于 2020-11-23T15:04:05.167 回答