最后的ContinueWith完成后如何从ConcurrentDictionary中删除项目

时间:2012-06-14 14:40:11

标签: task concurrentdictionary

首先,有1500+“声望”的人可以为“ContinueWith”创建一个标签(并用它标记这个问题)吗?谢谢!

很抱歉这篇文章的篇幅很长,但我不想浪费任何人试图帮助我的时间,因为我遗漏了相关细节。也就是说,它可能仍然会发生。 :)

现在的细节。我正在开发一个订阅几个ActiveMQ队列主题的服务。其中两个主题有些相关。一个是“公司更新”,一个是“产品更新”。两者的“ID”是CompanyID。 公司主题包含产品主题中的数据。必需,因为其他订阅者需要产品数据,但不希望/需要订阅产品主题。由于我的服务是多线程的(需要超出我们自行决定的范围),因此当消息到达时,我添加任务来使用AddOrUpdate处理 ConcurrentDictionary 中的每个消息,其中更新parm是只需 ContinueWith (见下文)。完成以防止可能发生的同时更新,因为这些主题和订阅者“持久”,因此如果我的侦听器服务脱机(无论什么原因),我们可能会以同一CompanyID的多条消息(公司和/或产品)结束。

现在,我的实际问题(终于!)完成任务后(无论是一个任务,还是ContinueWith任务链中的 last )完成后,我想将它从ConcurrentDictionary中删除(明显)。怎么样?我已经想过并从同事那里得到了一些想法,但我并不喜欢他们中的任何一个。我不打算列出这些想法,因为你的回答可能是我所拥有但不喜欢的一个想法,但它可能最终成为最好的想法。

我试图压缩代码段,以防止您不得不向上和向下滚动太多,这与我的描述不同。 :)

nrtq =与问题无关

public interface IMessage
{
  long CompantId { get; set; }
  void Process();
}
public class CompanyMessage : IMessage
{ //implementation, nrtq }
public class ProductMessage : IMessage
{ //implementation, nrtq }

public class Controller
{
  private static ConcurrentDictionary<long, Task> _workers = new ConcurrentDictionary<long, Task>();
  //other needed declarations, nrtq

  public Controller(){//constructor stuff, nrtq }

  public StartSubscribers()
  {
    //other code, nrtq
    _companySubscriber.OnMessageReceived += HandleCompanyMsg;
    _productSubscriber.OnMessageReceived += HandleProductMsg;
  }

  private void HandleCompanyMsg(string msg)
  {
    try {
      //other code, nrtq
      QueueItUp(new CompanyMessage(message));
    } catch (Exception ex) { //other code, nrtq }
  }

  private void HandleProductMsg(string msg)
  {
    try {
      //other code, nrtq
      QueueItUp(new ProductMessage(message));
    } catch (Exception ex) { //other code, nrtq }
  }

  private static void QueueItUp(IMessage message)
  {
    _workers.AddOrUpdate(message.CompanyId,
      x => {
        var task = new Task(message.Process);
        task.Start();
        return task;
      },
      (x, y) => y.ContinueWith((z) => message.Process())
    );
  }

谢谢!

1 个答案:

答案 0 :(得分:0)

我暂时不会“接受”这个答案,因为我很想知道是否还有其他人可以提出更好的解决方案。

一位同事提出了一个我稍微调整一下的解决方案。是的,我知道将lock语句与ConcurrentDictionary一起使用具有讽刺意义(?)。我现在没有时间看看是否有更好的收集类型可供使用。基本上,我们不是仅为现有任务执行ContinueWith(),而是使用ContinueWith()将任务替换为自身以及最后添加的另一项任务。

这有什么不同?很高兴你问! :)如果我们刚刚完成ContinueWith(),那么只要链中的第一个任务完成,!worker.Value.IsCompleted就会返回true。但是,通过用两个(或多个)链接任务替换任务,就集合而言,只有一个任务,而!worker.Value.IsCompleted将不会返回{{1直到链中的所有任务都完成。

我承认我有点担心用自己替换任务+(新任务),因为如果任务在被替换时碰巧正在运行。好吧,我测试了生活中的日光,并没有遇到任何问题。我相信正在发生的事情是,由于任务在其自己的线程中运行,并且集合只是持有指向它的指针,因此正在运行的任务不受影响。通过将其替换为自身+(新任务),我们维护指向执行线程的指针并在完成时获得“通知”,以便下一个任务可以“继续”或true返回IsCompleted

此外,“清理”循环的工作方式及其位置,意味着我们将在集合中挂起“已完成”的任务,但直到下次“清理”运行时才会下次收到消息时。再一次,我做了很多测试,看看我是否可能因此导致内存问题,但我的服务从未使用超过20 MB的RAM,即使在每秒处理数百条消息时也是如此。我们必须收到一些非常大的消息并且有很多长期运行的任务才能导致问题,但是由于您的情况可能会有所不同,因此需要牢记这一点。

如上所述,在下面的代码中,nrtq =与问题无关。

true