C#锁定和一般线程设计问题

时间:2009-08-19 19:40:53

标签: c# multithreading locking

我有一个运行良好的线程控制台应用程序,但它的架构需要改进,我想要一些反馈。

目前,程序会加载一个数据列表,并将该数据分段为分区(每个线程一个块)。然后,程序使用ThreadPool初始化一个新线程,并将其传递给要操作的分区数据的一段。

一切都很好......除了:

由于网络问题或无法恢复的异常,某些线程失败了。这是预期的行为而不是错误。

我现在需要一种方法(如果线程失败)恢复该线程的数据段并将其提供给另一个工作线程,以便它不会成为孤立的。我确信有办法做到这一点,即在线程之间共享数据等,但我认为有更好的方法。

我可以在所有线程之间共享一个静态的数据静态集合,而不是事先对数据进行分段并将其传递给每个线程。这更优雅,但引入了旧方法不必担心的新发现的同步问题。

A。)您对这种方法与旧方法的看法是什么? B.)如果这种方法很好,我该如何锁定对共享静态集合的访问。

当线程进入时,我可以锁定集合并弹出一段仅用于该线程的数据。静态集合现在将通过该线程弹出的数量减少。在线程失败后,我可以通过再次锁定它来将该数据段重新分配给共享集合,并将数据推送回集合以供其他线程尝试处理。

例如:(未经测试的伪代码)

void Process(object threadInfo)
{
  lock(StaticCollection)
  {
    var segment = StaticCollection.Take(100);
    StaticCollection.Remove(StaticCollection.Where(item => segment.Contains(item)))
  }

  foreach(var seg in segment)
  {
    // do something
  }

  // reallocate the thread's data on failure
  if(unrecoverableErrorOccurred)
  {
    lock(StaticCollection)
    {
      StaticCollection.Add(segment);
    }
  }
}

我是否在正确的轨道上?在我看来,一个线程可以同时删除项目另一个线程正在重新分配项目...或者对STATIC集合进行锁定意味着没有其他线程可以访问该集合。那么,Thread A.)在方法的FIRST部分获得了一个锁定,是否会阻止所有其他线程执行方法的LAST部分直到ThreadA完成?

2 个答案:

答案 0 :(得分:4)

让我们在这里分开一些事情......

首先,你不是实际锁定集合。您正在锁定与对象关联的监视器。我个人认为.NET在跟踪Java时给每个对象一个关联的监视器锁定是一个错误,但让我们把它留给一边。就个人而言,我更喜欢将对象和相关变量纯粹用于锁定 - 所以在我的代码中你可能会看到:

private readonly object padlock = new object();

这确保 no 其他代码将尝试获取该锁,因为他们不会知道该对象。

其次,锁是建议性的。这是“你没有锁定集合”的业务的一部分。如果集合本身在同一个锁上同步 - 并且非泛型集合有一个Synchronized方法用于此目的 - 但基本上除非某处明确取出锁定,否则您将无法获得同步。

第三,是的,代码中显示的两个锁定块是使用相同的锁(假设StaticCollection的值当然没有改变)。如果一个线程忙于调用Remove,那将阻止任何其他线程同时调用Add,因为它们都需要锁定。这可能就是你想要的。

我个人不会把它变成真正的静态集合(或者更确切地说,我不会使用StaticCollection变量)。我会给每个任务一个对同一个集合的引用(以及对相关锁的引用;实际上我可能封装了集合,同步和“给我一堆工作”和“这里有一堆工作到返回“在一个单独的类中的位”。这将使测试更简单,并且通常更好。这也意味着你可以有两个独立的“线程”集合同时处理不同的集合......如果你使上面的封装是通用的,这可能很有用,所以它们可以执行完全不同的任务......

答案 1 :(得分:0)

您可以考虑使用队列来保存未处理的块,并且正如Jon Skeet所说,锁定一个中性对象,并保持锁定的时间足够长以访问队列。我已经在很多线程中使用过这种方法,但它对我来说效果很好。