c# - 在 C#.NET 中将任意数量的任务链接在一起
问题描述
我有的
我有一套异步处理方法,类似于:
public class AsyncProcessor<T>
{
//...rest of members, etc.
public Task Process(T input)
{
//Some special processing, most likely inside a Task, so
//maybe spawn a new Task, etc.
Task task = Task.Run(/* maybe private method that does the processing*/);
return task;
}
}
我想要的是
我想将它们全部链接在一起,按顺序执行。
我试过的
我试图做以下事情:
public class CompositeAsyncProcessor<T>
{
private readonly IEnumerable<AsyncProcessor<T>> m_processors;
//Constructor receives the IEnumerable<AsyncProcessor<T>> and
//stores it in the field above.
public Task ProcessInput(T input)
{
Task chainedTask = Task.CompletedTask;
foreach (AsyncProcessor<T> processor in m_processors)
{
chainedTask = chainedTask.ContinueWith(t => processor.Process(input));
}
return chainedTask;
}
}
什么地方出了错
但是,任务不会按顺序运行,因为据我所知,在对 的调用中ContinueWith
,processor.Process(input)
调用会立即执行,并且该方法会独立于返回的任务的状态返回。因此,所有处理任务仍然几乎同时开始。
我的问题
我的问题是我是否可以做一些优雅的事情来按顺序链接任务(即没有执行重叠)。例如,我可以使用以下语句来实现这一点(我在细节上有点挣扎)吗?
chainedTask = chainedTask.ContinueWith(async t => await processor.Process(input));
另外,如果不使用 async/await,我将如何做到这一点ContinueWith
?
我为什么要这样做?
因为我的Processor
对象可以访问和请求“线程不安全”资源。另外,我不能只是等待所有方法,因为我不知道它们有多少,所以我不能只写下必要的代码行。
线程不安全是什么意思?一个具体的问题
因为我可能错误地使用了这个术语,所以用一个插图来解释这一点会更好一些。在我的Processor
对象使用的“资源”中,它们都可以访问如下对象:
public interface IRepository
{
void Add(object obj);
bool Remove(object obj);
IEnumerable<object> Items { get; }
}
目前使用的实现比较幼稚。因此,有些Processor
对象添加了东西,而另一些对象则检索了Items
以供检查。当然,我经常遇到的例外之一是:
InvalidOperationException
: 集合被修改,枚举操作可能无法执行。
我可以花一些时间锁定访问和预运行枚举。然而,这是我要考虑的第二个选项,而我的第一个想法是让进程按顺序运行。
为什么我必须使用任务?
虽然在这种情况下我可以完全控制,但我可以说,出于问题的目的,我可能无法更改基本实现,那么如果我被任务卡住会发生什么?此外,这些操作实际上确实代表了相对耗时的 CPU 密集型操作,而且我正在尝试实现响应式用户界面,因此我需要减轻异步操作的一些负担。虽然很有用,并且在我的大多数用例中,没有必要链接多个,而是每次链接一个(或几个,但总是特定的和特定的计数,所以我能够挂钩它们一起没有迭代和异步/等待),其中一个用例最终需要将未知数量的任务链接在一起。
我目前如何处理
我目前处理这个问题的方式是将调用附加到调用Wait()
内部ContinueWith
,即:
foreach (AsyncProcessor<T> processor in m_processors)
{
chainedTask = chainedTask.ContinueWith(t => processor.Process(input).Wait());
}
我将不胜感激任何关于我应该如何做到这一点的想法,或者我如何能更优雅地做到这一点(或者,可以说是“异步正确”)。另外,我想知道如何在没有 async/await的情况下做到这一点。
为什么我的问题与这个问题不同,后者并没有完全回答我的问题。
因为链接的问题有两个任务,所以解决方案是简单地编写所需的两行,而我有任意(和未知)数量的任务,所以我需要一个合适的迭代。另外,我的方法不是 async。我现在明白(从已删除的单个简要可用答案中)如果我将方法更改为async
和await
每个处理器的Task
方法,我可以相当容易地做到这一点,但我仍然想知道如何在没有 async/await 语法的情况下实现这一点。
为什么我的问题不是其他链接问题的重复
因为他们都没有解释如何正确使用链接ContinueWith
,而且我对使用ContinueWith
和不使用 async/await 模式的解决方案感兴趣。我知道这种模式可能是更好的解决方案,我想了解如何(如果可能)ContinueWith
正确使用调用进行任意链接。我现在知道我不需要ContinueWith
。问题是,我该怎么做ContinueWith
?
解决方案
foreach
+将按顺序await
运行es。Process
public async Task ProcessInputAsync(T input)
{
foreach (var processor in m_processors)
{
await processor.Process(input));
}
}
顺便提一句。Process
, 应该称为ProcessAsync
推荐阅读
- ag-grid - 在chrome 81版本中,设置ag-grid的gridOptions.defaultColDef.autoHeight = true,调用api.resetRowHeights时浏览器崩溃或卡住
- php - laravel auth()->attempt() 是否写入任何会话
- html-imports - 使用 webcomponents 后,HTML 导入在 Chrome 最新版本中不起作用
- php - 如何使用 jquery ajax 进行通知
- javascript - vue-test-utils - 如何处理 $refs?
- python - 提取python中三个星号之间的数据
- mysql - 大 INSERT 的性能与来自大量选择的数据
- python - 使用递归和线程,如何避免“致命的 Python 错误:无法从堆栈溢出中恢复。线程 0x00007d54”?
- java - 每个节点有多少位
- vba - 从 ReceivedTime 查找电子邮件