使用任务并行库实现“仅其中之一”和“不并行”语义
-
26-09-2019 - |
题
使用 KEY 执行任务的最佳方法是什么,操作如下:-
选项 1) 此密钥中只有一个处于待处理状态。例如,可以在 ASP.NET MVC 中使用来对缩略图图像的单个渲染进行排队,无论图像 Url 被点击多少次。只有一个请求运行,所有其他请求都等待该请求完成。
选项 2) 具有相同键的所有项目必须按顺序执行。例如,可用于确保从后备存储获取文件到本地缓存的操作不会同时尝试将文件获取到缓存。选项 1 是一种特殊情况,其中具有相同密钥的后续操作将被简单地丢弃(通常仅保存文件存在检查)。
我有一个现有的 WorkQueue 可以处理这两种情况(以及 Apartment 状态、ThreadPriority 设置和最大并行度)。TPL 似乎是替代此问题的最佳解决方案,并将带来改进的取消选项。
具有延续性的嵌套任务看起来很有希望,但维护当前排队任务的字典很快就会在 TaskFactory 和 TaskScheduler 类之间变得混乱。从 Task 继承也是有问题的,因为 TaskFactory 和 TaskScheduler 在 Task 上都不是通用的。
大多数任务并行示例假设任务集是提前已知的。在这种情况下,总是会添加新任务,并且需要根据请求的操作和传入的密钥将其丢弃或链接到现有任务。
有没有人使用 TPL 实现了类似的东西,如果是,您在 Task、TaskScheduler 和 TaskFactory 类中采用了什么方法?
解决方案
也许,我能想到的一种方法是
- 创建一个包装类 - 例如 KeyProcessor 来对键的项目进行排队。
- KeyProcessor.Run() 方法将能够实现您需要的任何排队语义。本质上,它会在内部队列中查找任何待处理的工作,然后继续按顺序执行。
- 维护 KeyProcessor 对象的字典。
- 对于任何新任务,请在字典中检查相同的键。如果不存在则添加它。将任务排队在上面。如果它没有运行,则使用 Run 方法作为操作通过 TPL 安排它。
- 使用ContinueWith来安排维护者任务 - 例如,每当执行KeyProcessor.Run的任务完成时,延续任务可以检查是否有为同一键安排的更多任务(因为它已经完成)并再次启动它或从字典中删除。
如果不是因为其中存在一些有趣的集合,以上所有内容从线程同步点来看都是很棘手的 系统.集合.并发 命名空间。这将使上面的逻辑变得更加简单。例如, ConcurrentDictionary.GetOrAdd 将允许以线程安全的方式查找和/或添加 KeyProcessor 对象。
其他提示
此问题类似于一个我在解决 ReactiveXaml ,虽然雷也memoized以前的请求。看看为 QueuedAsyncMRUCache 代码(和其博客条目) - 此代码与无扩展的TPL得到这样的东西做联合收割机,但它使对于相同的钥匙2号请求将阻止第一飞行请求而不是重要保证发出另一个。