【问题标题】:Implementing 'only one of' and 'not in parallel' semantics with the Task parallel library使用任务并行库实现“仅一个”和“不并行”语义
【发布时间】:2010-09-01 14:18:50
【问题描述】:

使用如下操作的 KEY 实施任务的最佳方法是什么:-

选项 1) 此密钥中只有一个处于待处理状态。例如,可用于从 ASP.NET MVC 为缩略图图像排队单个渲染,无论图像 Url 被点击多少次。只有一个运行,所有其他请求等待那个完成。

选项 2) 具有相同键的所有项目必须按顺序执行。例如,可用于确保从后备存储获取文件到本地缓存的操作不会同时尝试将文件获取到缓存。选项 1 是这种情况的一种特殊情况,其中具有相同键的后续操作被简单地丢弃(通常只保存文件存在检查)。

我有一个现有的 WorkQueue 可以处理这两种情况(以及 Apartment 状态、ThreadPriority 设置和最大并行度)。 TPL 似乎是替代它的最佳解决方案,并将带来改进的取消选项。

具有延续性的嵌套任务看起来很有希望,但维护当前排队任务的字典很快就会在 TaskFactory 和 TaskScheduler 类之间变得混乱。从 Task 继承也是有问题的,因为 TaskFactory 和 TaskScheduler 在 Task 上都不是通用的。

大多数任务并行示例假设任务集是提前知道的。在这种情况下,新任务一直被添加,需要根据请求的操作和传入的密钥被丢弃或链接到现有任务。

有没有人使用 TPL 实现过类似的东西,如果有,您在 Task、TaskScheduler 和 TaskFactory 类中采用了什么方法?

【问题讨论】:

    标签: c# .net task-parallel-library


    【解决方案1】:

    也许,我能想到的一种方法是

    1. 创建一个包装类 - 比如说 KeyProcessor 来为一个键排队项目。
    2. KeyProcessor.Run() 方法将能够处理您需要的任何排队语义。本质上,它会为任何待处理的工作寻找内部队列,然后继续按顺序执行。
    3. 维护 KeyProcessor 对象的字典。
    4. 对于任何新任务,请在字典中查找相同的键。如果不存在则添加它。排队任务就可以了。如果它没有运行,则使用 Run 方法作为操作使用 TPL 对其进行调度。
    5. 使用 ContinueWith 来安排维护者任务 - 例如,每当执行 KeyProcessor.Run 的任务完成时,延续任务可以检查是否有更多任务为同一个键安排(因为它已完成)并重新启动它或删除来自字典。

    如果System.Collections.Concurrent 命名空间中存在一些有趣的集合,从线程同步点开始,以上所有内容都会变得很棘手。这将使上述逻辑更加简单。例如,ConcurrentDictionary.GetOrAdd 将允许以线程安全的方式查找和/或添加 KeyProcessor 对象。

    【讨论】:

    • 使用 Continuations 的自定义任务工厂能够解决此问题。博客文章在这里:blog.abodit.com/2010/09/…。因为你的建议很接近,所以我会给你答案分数。
    【解决方案2】:

    这个问题类似于我在ReactiveXaml 中解决的问题,尽管我的也记住了以前的请求。看一下QueuedAsyncMRUCache(及其blog entry)的代码——这段代码结合了TPL和响应式扩展来完成这种事情,但它保证了对相同密钥的第二次请求将阻止第一个进行中的请求,而不是发出另一个请求。

    【讨论】:

    • 谢谢,很有趣,但不是我需要的。我应该补充一点,我希望能够触发并忘记以及触发并等待添加的任何任务。我还希望在执行最后一个密钥后立即删除密钥。例如更新网站上用户的上次访问时间:五个同时点击被折叠为一个写入,但稍后的点击被记录。
    • 或许至少你可以从我实现它的方式中得到一些启发
    • 有趣的是,我最终实现了您为项目选项 #2 指定的大部分内容:gist.github.com/2877c8181d5ae4c8ac02
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多