2009-09-19 204 views
5

我在.net/C#中被问及这个问题:交易 - 如何避免死锁?

如果我们有两个线程T1和T2。 T1 获取obj1上的锁定,然后执行 某些处理并获取 obj2上的锁定。 T2获得对obj2的锁定并且 然后进行一些处理并获取 对obj1的锁定。所以,我们可以有一个 死锁。我们在多线程中使用的常见技术 是什么,以避免 这种情况?

我回答说T1和T2应该有一定的沟通机制,我们应该这样编码,使得T2只有在T1发出信号后才开始工作。面试官问我是否知道交易以及如何使用它来解决这种僵局。 我在winforms的UI方面有一定的多线程经验。但是,我从来没有使用过交易。有人可以告诉我更多关于此的信息,或者将我指向url/book,

回答

7

避免死锁的一种常规方法是确保您的线程/进程以相同的顺序获取资源上的锁。例如,T2应该首先锁定obj1,然后obj2(与T1相同)。

这样你就不能让两个线程持有另一个线程想要的资源,即死锁。

如果您的报价中的措词是确切的问题,那么写得很糟糕。它应该是:

T1获取锁OBJ1,确实 东西,然后试图锁定也 obj2的未解锁OBJ1。 同时 T2获取 对obj2的锁定,做一些事情,然后 然后试图在没有解锁obj2的情况下锁定obj1。 A会发生死锁。

我强烈推荐阅读Joe Duffy的Concurrent Programming on Windows。这可能是关于Windows线程理论和实践的最全面的书。

+0

@Ash我得到了以相同的顺序获取锁定点。但是,那么关于交易的是什么呢? – Sandbox 2009-09-19 10:32:17

+1

@Sandbox,对我来说,他们看起来像是在谈论一个“事务”,一般意义上说是确保一系列不同的操作是作为一个(即原子)执行的。在数据库中,这是通过Begin Transaction关键字在.net中实现的,它通过(通常)使用lock语句来实现。我在答复中添加了Joe Duffy强烈推荐的书。 – Ash 2009-09-19 10:56:58

3

一种方法是所有进程都需要在事务开始时获取所有锁。如果某些不可用,该进程将释放所有锁,然后重试。根据实施情况,这仍然可能导致活锁。

要从另一端看问题,请参阅Dijkstra's banker's algorithm

1

我并不完全确定交易到哪里直接进去了,除了回滚的能力。国际海事组织的主要目的是以一致的顺序获得锁具,并提前;哦,并在获取锁定时使用超时 - 不要坐在那里看起来永远卡住。

的交易参考让我首先想到的数据库,在这种情况下,另一个考虑是使用的技巧,比如UPDLOCK,以确保你得到一个写锁最初以避免促进(有争议)读锁问题写-lock(从死锁变为简单的阻塞)。当然,许多数据库也比大多数常规代码具有更好的死锁检测。

+0

我认为术语“事务”通常意味着确保多个非原子操作按原子执行。通常的解决方案是使用锁定。 – Zed 2009-09-19 07:15:53

1

我认为面试官所指的是回滚事务的能力。事务的回滚将恢复事务所做的所有更改,就好像事务从未发生过一样。它也将释放它获得的所有锁。

现在让示例中的每个线程使用自己的事务(在数据库,Vista文件系统或支持事务的其他接口中)。如果发生死锁(一旦发生死锁就可以轻松检测到),您将从线程中选择一个死锁(受害者)的一部分并回滚其事务。这将释放锁,以便剩下的线程可以继续。受害者线程可能会重试该事务。

如果死锁的概率低于重试整个事务的成本,这可能是可用的解决方案。