我在.net / C#面试中被问到这个问题:
如果我们有两个线程T1和T2。 T1 获取对obj1的锁定然后执行 一些处理并获得锁定 obj2的。 T2获取对obj2和的锁定 然后进行一些处理和获取 锁定obj1。所以,我们可以有一个 僵局。什么是常用技术 我们在多线程中使用以避免 这种情况?
我回答说T1和T2应该有一些通信机制,我们应该以这样的方式进行编码,即只有在T1发出信号完成后才开始工作。面试官问我是否了解交易以及我们如何使用它来解决这种僵局。 我在winforms的UI端有一些多线程的经验。但是,我从未使用过交易。有人可以告诉我更多关于这一点或指向我的网址/书,
答案 0 :(得分:7)
避免死锁的一种通用方法是确保线程/进程以相同的顺序获取资源锁。例如,T2应先锁定obj1然后再锁定obj2(与T1相同)。
这样你就不能让两个线程都拥有另一个线程想要的资源,即死锁。
如果你的引文中的措辞是确切的问题,那就写得很糟糕。它应该是:
T1获取对obj1的锁定,确实如此 一些东西,然后试图也锁定 obj2没有解锁obj1。 同时 T2获得 锁定obj2,做一些事情 然后尝试在没有解锁obj2的情况下锁定obj1。一个 会发生僵局。
我强烈推荐Joe Duffy阅读Concurrent Programming on Windows。它可能是关于Windows线程理论和实践的最全面的书。
答案 1 :(得分:3)
一种方法是所有进程都需要在事务开始时获取所有锁。如果某些不可用,则该进程将释放所有锁定并重试。根据实施情况,这仍然会导致活锁。
要从另一端查看问题,请参阅Dijkstra's banker's algorithm。
答案 2 :(得分:1)
除了回滚功能之外,我不能完全确定事务直接到哪里。 IMO的主要任务是尽早以一致的顺序获取锁;哦,在获取锁定时使用超时 - 不要坐在那里看起来永远陷入困境。
对事务的引用使我主要考虑数据库,在这种情况下,另一个考虑因素是使用诸如UPDLOCK
之类的技巧来确保您最初获得写入锁 以避免问题促进(有争议的)对写锁的读锁(从死锁变为简单阻塞)。当然,许多数据库也比大多数常规代码具有更好的死锁检测功能。
答案 3 :(得分:1)
我认为,采访者所指的是回滚交易的能力。回滚事务将还原事务所做的所有更改,就好像事务从未发生过一样。它还将释放由它获得的所有锁。
现在让示例中的每个线程使用自己的事务(在数据库,Vista文件系统或其他支持事务的接口中)。如果发生死锁(一旦发生就可以轻松检测到),您将从作为死锁(受害者)一部分的线程中选择一个并回滚其事务。这将释放锁,以便剩余的线程可以继续。受害者线程可能会在后面重试该事务。
如果死锁的概率相对于重试整个事务的成本较低,这可能是可用的解决方案。