单线程应用程序中的死锁
Posted
技术标签:
【中文标题】单线程应用程序中的死锁【英文标题】:Deadlock in Single threaded application 【发布时间】:2010-01-29 15:32:11 【问题描述】:单线程应用程序会出现死锁吗?如果有,请举个例子。
【问题讨论】:
【参考方案1】:是的,如果您拥有不可重入的锁并且线程尝试重新获取它已经拥有的锁(例如通过递归调用),单线程应用程序可能会死锁。
编辑:我看到帖子被标记为“Java”;我不知道那是更新还是我之前错过了,但无论如何 Java 中的锁都是可重入的,因此您无法以这种方式死锁单线程应用程序。
【讨论】:
通常,这会导致某种错误,表明线程已经拥有锁。 那不取决于平台吗? 它可能会锁定,但根据术语的定义,这不会是死锁。诚然,由于没有更好的词,我可能会称之为僵局:) "...如果您有线程,单线程应用程序可能会死锁..." - 听起来像是一个矛盾的术语。根据定义,单线程应用程序没有线程。 @atzz:首先,单线程应用程序只有一个线程,而不是没有线程。其次,我的帖子显然是指线程是否可以重新获取特定平台中的锁。您的反对意见是不明智的。【参考方案2】:是的,如果应用程序与另一个应用程序共享资源,它可能会死锁。
【讨论】:
好点。这是否是预期的答案取决于确切的原始措辞,但正如这里所写,这将是最好的答案。【参考方案3】:这在一定程度上取决于您如何解释问题。如果涉及与其他应用程序共享的资源,则可以。没有共享资源,不行。
死锁是指两个或多个相互竞争的动作正在等待另一个完成,因此永远都不会。
单线程,只有一个动作,无法与自己竞争。
来自***:
必要条件
发生死锁有四个充分必要条件,称为 科夫曼条件来自 E. G. Coffman 1971 年的一篇文章中的首次描述。
互斥条件:一次不能被多个进程使用的资源 持有和等待条件:已经持有资源的进程可能会请求新资源 无抢占条件:无法从持有它的进程中强制移除资源,只能通过进程的显式操作释放资源 循环等待条件:两个或多个进程形成一个循环链,其中每个进程等待链中下一个进程持有的资源
根据这个定义,需要两个进程来构成死锁。
【讨论】:
您的意思是“在 Java 中”,对吗?否则,MSDN 不同意您的看法。 msdn.microsoft.com/en-us/library/ms792855.aspx 我的意思是一般的计算机科学。 那个链接描述了一个可能发生的场景——你不同意吗? @danben:我不同意所描述的案例不应该被称为死锁(根据引用的定义)。因此,不提这个案子至少是可以辩护的,特别是如果有一个更简单的死锁发生的例子。 好吧,如果你坚持使用***的文章作为死锁的权威,那么你也不得不说单个多线程Java应用程序永远不会死锁,因为***声称死锁只发生在两个进程之间,并且线程不是进程。在某些时候,开始质疑你的定义可能是谨慎的。【参考方案4】:线程 A 获取资源 1,并尝试重新获取资源 1。 自循环情况。
线程获取 lock1 -> 在临界区运行 -> 尝试获取 lock1 -> 无限等待 == 自死锁。 为了解决这个问题,你需要递归锁。
【讨论】:
【参考方案5】:是的,单线程事件驱动(非阻塞)应用程序肯定会死锁。即使没有任何操作系统同步原语,如互斥锁,甚至根本没有操作系统。在 FSM(有限状态机)设计中,死锁是常见的错误。
假设您有一个只能通过 write(COMMAND) 然后 read(RESP) 模式访问的串行设备。所以要同时访问它你需要一些序列化技术。最简单的方法是排队。 这是 javascript 中此类队列的最简单实现:
function SharedResource()
var self =
queue_: [],
acquire: function(on_sharedResource_ready)
var n = self.queue_.push(on_sharedResource_ready);
if(n==1) // first task
on_sharedResource_ready();
,
release: function()
if(self.queue_.length >= 1)
// Pop current task
self.queue_.shift();
// Exec the next task
var next = self.queue_[0];
if(next)
next();
else
throw 'SharedResource::release(): BUG - queue is empty';
,
;
return self;
我们可以在 button_click 事件中使用它,例如:
var serialPort1 = SharedResource();
var serialPort2 = SharedResource();
function button1_on_click()
log("button1_on_click(): Acquiring serial port 1");
serialPort1.acquire(function()
log("button1 Serial port 1 acquired");
setTimeout(function()
log("button1: Acquiring serial port 2");
serialPort2.acquire(function()
log("button1 Serial port 2 acquired");
// Simulate long time work with a ports
setTimeout(on_work_with_serial_port_done, 2000);
);
, 1000);
);
function on_work_with_serial_port_done()
log("button1 Releasing serial port 2");
serialPort2.release();
log("button1 Releasing serial port 1");
serialPort1.release();
function button2_on_click()
log("button2_on_click(): Acquiring serial port 2");
serialPort2.acquire(function()
log("button2 Serial port 2 acquired");
setTimeout(function()
log("button2: Acquiring serial port 1");
serialPort1.acquire(function()
log("button2 Serial port 1 acquired");
// Simulate long time work with a ports
setTimeout(on_work_with_serial_port_done, 2000);
);
, 1000);
);
function on_work_with_serial_port_done()
log("button2 Releasing serial port 1");
serialPort1.release();
log("button2 Releasing serial port 2");
serialPort2.release();
以及让它工作的其余代码:
function getTime()
var today = new Date();
var time = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
return time;
// simple logger
function log(text)
var logdiv = document.getElementById('log');
var br = document.createElement("br");
var t = document.createTextNode(getTime()+ ": " + text);
logdiv.appendChild(t);
logdiv.appendChild(br);
// register event handlers
window.onload = function ()
var btn1 = document.getElementById('button1');
btn1.onclick = button1_on_click;
var btn2 = document.getElementById('button2');
btn2.onclick = button2_on_click;
;
<html><head><script src="deadlock.js"></script></head><body>
<input type="button" value="Do work1" id="button1">
<input type="button" value="Do work2" id="button2">
<div id="log"></div>
</body></html>
如果我们按下按钮 1 并在超过 1 秒后按下按钮 2 一切都会正常工作:
16:12:20: button1_on_click(): Acquiring serial port 1
16:12:20: button1 Serial port 1 acquired
16:12:21: button1: Acquiring serial port 2
16:12:21: button1 Serial port 2 acquired
16:12:21: button2_on_click(): Acquiring serial port 2
16:12:23: button1 Releasing serial port 2
16:12:23: button2 Serial port 2 acquired
16:12:23: button1 Releasing serial port 1
16:12:24: button2: Acquiring serial port 1
16:12:24: button2 Serial port 1 acquired
16:12:26: button2 Releasing serial port 1
16:12:26: button2 Releasing serial port 2
但是如果我们按下 button1 然后 button2 快速应用程序将死锁并且不再响应 button1 和 button2 的点击
16:14:28: button1_on_click(): Acquiring serial port 1
16:14:28: button1 Serial port 1 acquired
16:14:28: button2_on_click(): Acquiring serial port 2
16:14:28: button2 Serial port 2 acquired
16:14:29: button1: Acquiring serial port 2
16:14:29: button2: Acquiring serial port 1
16:14:41: button1_on_click(): Acquiring serial port 1
16:14:41: button2_on_click(): Acquiring serial port 2
16:14:41: button1_on_click(): Acquiring serial port 1
16:14:42: button2_on_click(): Acquiring serial port 2
16:14:42: button1_on_click(): Acquiring serial port 1
16:14:42: button2_on_click(): Acquiring serial port 2
16:14:45: button2_on_click(): Acquiring serial port 2
16:14:45: button2_on_click(): Acquiring serial port 2
16:14:46: button1_on_click(): Acquiring serial port 1
16:14:46: button1_on_click(): Acquiring serial port 1
16:14:47: button1_on_click(): Acquiring serial port 1
当然,我们的应用程序仍然可以处理其他事件,例如 button3。在多线程应用中情况完全一样——当thread1和thread2相互死锁时,thread3仍然可以工作。
【讨论】:
以上是关于单线程应用程序中的死锁的主要内容,如果未能解决你的问题,请参考以下文章
由 Dispatcher.Invoke 从多个线程调用的代码中的死锁