取消并行进程
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.ForEach
is 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();根据代码,它不应该在执行第一秒后返回值。但仍在后台运行作业。以上是关于取消并行进程的主要内容,如果未能解决你的问题,请参考以下文章
多进程取消链接共享内存,需要只有在所有进程退出时才应该取消链接共享内存