工作中常常出现死锁这种说法,本喵刚参加工作的时候呢总觉得大家嘴里的死锁根本不是教材和文档中讲到的死锁,还因此和同事严肃争论过。后来反思一下,同事说的deadlock也不无道理,因此把教科书中的死锁和工作中常常说的另一种死锁一起总结一下。
情况1:相互等待
一般官方的文档和权威教材中,deadlock一般是指有至少两方,发生相互等待的场景。这里枚举几个官方讲死锁的场景。
这里举个栗子网络编程大神Richard Stevens在《TCP/IP illustrated》中写到的一种死锁(当然TCP的设计者通过优化TCP协议早就解决了这个问题,这里还是可以拿这个场景来说明问题)。当TCP的一方read buffer full时,会在ack收到这些数据的同时发给对方window = 0; 也就是暂时关闭窗口,毕竟在用户进程读走buffer之前kernel的TCP程序无法处理更多的数据了。
然后过了好一会当buffer中的数据被用户进程读完之后,TCP端会再次发一个ack同时传递window = 正常值比如1024。但是这是一个二次ack的信息,而ack是不会再次被ack的,所以这个发送的ack如果被网络丢失了,发送方并不知道。于是乎ack发送方在等对方发送数据,而对方却因为没有收到window再次打开的ack信息而等待对方“开窗”。于是双方就陷入了互相等的尴尬场景,这就是一种典型的死锁问题。
情况2:单相思
第二种情况文档资料中一般不会讲,更多是业界根据实际问题起得名字。笔者刚刚工作的时候就有点书呆子气,觉得这明明不是相互等待为什么也叫死锁。下面讲一讲这个比较写实名字的含义和来源。
这种死锁的情况不再是两边相互等待,而是有一方霸占了代表某种资源或者特权的锁,另一方在苦苦的等在对同一种资源的使用权也就是这个资源的锁,但由于这种苦等是单相思另一方并不会真的释放资源陷入了程序hang住的情况。由于这种情况是对“锁”的“死等”,所以业界常常非常形象的成为这种情况为死锁。
举个例子来说,mysql中经常出现for update或for share的语句失败之后没有rollback的,导致该客户端一直占用行的写锁。其他client的locking-reads就会出现死等这个行锁,于是出现死锁。
这种情况下的死锁往往是针对真的有”锁“的场景下的称呼,为什么这么说呢?其实一方等待一方不永久性不回应是很常见的事,但一般只有在mysql锁等有锁的场景才会被这么称呼。比如TCP中client断电,在没有keep-alive probe的场景下server永久性等待client发信息,这种场景其实本质上和上面死锁的例子是一个道理,但却鲜有人称这种场景为死”锁“。
对比一下
注意哦,第一种情况(关键词相互等待)中并不一定真的有锁,往往是一种写意的说法。比如TCP的例子里的deadlock,并没有”lock“机制。相互等待的问题更多是设计上的,是一种相对高级的失误。第二种情况(关键词单相思)中确实有lock,实际是程序员们在解决问题中自己叫的一种说法,比较写实。出现原因往往是一些使用上的低级错误,可能也就是因为这个原因一般教材不会讲这种case。