我有一个运行良好的线程控制台应用程序,但它的架构需要改进,我想要一些反馈。
目前,程序会加载一个数据列表,并将该数据分段为分区(每个线程一个块)。然后,程序使用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完成?
答案 0 :(得分:4)
让我们在这里分开一些事情......
首先,你不是实际锁定集合。您正在锁定与对象关联的监视器。我个人认为.NET在跟踪Java时给每个对象一个关联的监视器锁定是一个错误,但让我们把它留给一边。就个人而言,我更喜欢将对象和相关变量纯粹用于锁定 - 所以在我的代码中你可能会看到:
private readonly object padlock = new object();
这确保 no 其他代码将尝试获取该锁,因为他们不会知道该对象。
其次,锁是建议性的。这是“你没有锁定集合”的业务的一部分。如果集合本身在同一个锁上同步 - 并且非泛型集合有一个Synchronized
方法用于此目的 - 但基本上除非某处明确取出锁定,否则您将无法获得同步。
第三,是的,代码中显示的两个锁定块是使用相同的锁(假设StaticCollection
的值当然没有改变)。如果一个线程忙于调用Remove
,那将阻止任何其他线程同时调用Add
,因为它们都需要锁定。这可能就是你想要的。
我个人不会把它变成真正的静态集合(或者更确切地说,我不会使用StaticCollection
变量)。我会给每个任务一个对同一个集合的引用(以及对相关锁的引用;实际上我可能封装了集合,同步和“给我一堆工作”和“这里有一堆工作到返回“在一个单独的类中的位”。这将使测试更简单,并且通常更好。这也意味着你可以有两个独立的“线程”集合同时处理不同的集合......如果你使上面的封装是通用的,这可能很有用,所以它们可以执行完全不同的任务......
答案 1 :(得分:0)
您可以考虑使用队列来保存未处理的块,并且正如Jon Skeet所说,锁定一个中性对象,并保持锁定的时间足够长以访问队列。我已经在很多线程中使用过这种方法,但它对我来说效果很好。