单线程应用程序中的死锁

时间:2010-01-29 15:32:11

标签: multithreading deadlock single-threaded

单线程应用程序是否可以出现死锁?如果是,请提供一个例子。

5 个答案:

答案 0 :(得分:6)

是的,如果你有一些不可重入的锁并且一个线程试图重新获得它已经拥有的锁(例如通过递归调用),单线程应用程序就会死锁。

编辑:我看到该帖子已被标记为“Java”;我不知道这是否是更新,或者我之前是否错过了,但无论如何,Java中的锁都是可重入的,因此您将无法以这种方式将单线程应用程序死锁。

答案 1 :(得分:5)

这取决于你如何解释这个问题。如果涉及与其他应用程序共享的资源,则为是。没有共享资源,没有。

  

死锁是指两个或多个竞争行为正在等待另一个完成的情况,因此两者都没有。

使用单个线程,只有一个操作,无法与自身竞争。

来自维基百科:

  

必要条件

     

发生死锁有四个必要且充分的条件,称为   科夫曼的条件来自于E. G. Coffman在1971年的一篇文章中的第一次描述。

     
      
  1. 互斥条件:一次不能由多个进程使用的资源
  2.   
  3. 保持和等待条件:已持有资源的流程可能会请求新资源
  4.   
  5. 没有抢占条件:没有资源可以从持有它的进程中强行删除,资源只能通过进程的显式操作释放
  6.   
  7. 循环等待条件:两个或多个进程形成一个循环链,每个进程等待链中下一个进程持有的资源
  8.   

根据这个定义,需要两个进程才能构成死锁。

答案 2 :(得分:5)

是的,如果应用程序与另一个应用程序共享资源,它可能会陷入僵局。

答案 3 :(得分:1)

是的,单线程事件驱动(非阻塞)应用肯定会死锁。即使没有任何操作系统同步原语,如互斥锁,甚至根本没有操作系统。在 FSM(有限状态机)设计中死锁是常见的错误。

假设您有一个只能通过写(命令)然后读(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。在多线程应用中情况完全相同——当线程 1 和线程 2 相互死锁时,线程 3 仍然可以工作。

答案 4 :(得分:0)

线程A获取资源1,并尝试重新获取资源1。 自我循环的情况。

线程获取lock1 - &gt;在关键部分运行 - &gt;试图获得lock1 - &gt;无限等待==自我死锁。 要解决这个问题,你需要递归锁。