单线程应用程序会出现死锁吗?如果有,请举个例子。
5 回答
是的,如果您拥有不可重入的锁并且线程尝试重新获取它已经拥有的锁(例如通过递归调用),则单线程应用程序可能会死锁。
编辑:我看到帖子被标记为“Java”;我不知道这是更新还是我之前错过了它,但无论如何,Java 中的锁都是可重入的,因此您将无法以这种方式死锁单线程应用程序。
是的,如果应用程序与另一个应用程序共享资源,它可能会死锁。
这在一定程度上取决于您如何解释这个问题。如果涉及与其他应用程序共享的资源,则可以。没有共享资源,没有。
死锁是一种情况,其中两个或多个竞争动作正在等待另一个完成,因此永远不会。
单线程,只有一个动作,无法与自己竞争。
来自维基百科:
必要条件
发生死锁有四个必要和充分条件,即 EG Coffman 在 1971 年的一篇文章中首次描述的 Coffman 条件。
- 互斥条件:一次不能被多个进程使用的资源
- 持有和等待条件:已经持有资源的进程可能会请求新的资源
- 无抢占条件:任何资源都不能从持有它的进程中强制移除,资源只能通过进程的显式动作释放
- 循环等待条件:两个或多个进程形成一个循环链,其中每个进程等待链中下一个进程持有的资源
根据这个定义,需要两个进程来构成死锁。
线程 A 获取资源 1,并尝试重新获取资源 1。自循环情况。
线程获取 lock1 -> 在临界区运行 -> 尝试获取 lock1 -> 无限等待 == 自死锁。为了解决这个问题,你需要递归锁。
是的,单线程事件驱动(非阻塞)应用程序肯定会死锁。即使没有任何操作系统同步原语,如互斥锁,甚至根本没有操作系统。在 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
但是如果我们快速按下按钮 1 然后按钮 2应用程序将死锁并且不会再响应按钮 1 和按钮 2 的点击
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仍然可以工作。