创建死锁的最短代码


11

编写最短的代码以创建死锁。代码执行必须停止,所以这不起作用:

public class DeadlockFail extends Thread{ //Java code
    public static void main(String[]a){
        Thread t = new DeadlockFail();
        t.start();
        t.join();
    }
    //this part is an infinite loop; continues running the loop. 
    public void run(){while(true){}}
}

它并不需要是肯定的代码进入僵局,就几乎可以肯定(如果你运行了无限的时间,它会死锁)。


如果我尝试从同一线程两次锁定同一(不可重入)锁定,是否算作死锁?(对不起,我没有在沙盒中意识到这个问题)
John Dvorak

@JanDvorak是否会因为一个线程正在等待它无法获取的内容而导致代码执行停止(因为另一个线程正在持有它并等待第一个线程所拥有的内容)?还是一个线程?如果您可以用一个线程创建这种情况,那就很好。阅读有关死锁的wikepedia文章,这是我所期望的。
贾斯汀

2
Code execution must halt我不明白 如果停止,它将如何陷入僵局?您是说它会等待某件事,而不只是像个混蛋那样自旋吗?
Cruncher

@Cruncher看看不起作用的示例。由于循环不断运行,因此代码执行不会停止。是的,我的意思是等待而不是旋转。
贾斯汀

因此,如果您可以让Dyalog APL中的UI线程等待自身,这是否意味着有希望获得JavaScript答案?尽管我怀疑这种想法只会打开答案的门:Javascript 6“ wait()” ... ermmm。没有。相关:线程是否有可能自行死锁?
内森·库珀

Answers:


11

Dyalog APL(10)

⎕TSYNC⎕TID

⎕TSYNC使线程等待,直到给定线程结束, ⎕TID给出当前线程。

Dyalog APL可以识别死锁,因此它会立即响应

DEADLOCK

有趣的是,您甚至不需要生成任何额外的线程,从而使UI线程等待大量时间。

如果这是作弊行为,并且实际上需要新线程,则可以用27个字符进行操作:

{∇⎕TSYNC&{⎕TSYNC⎕TID+1}&0}0

F & xF在value的新线程中运行x,并返回线程ID。所以:

  • {⎕TSYNC⎕TID+1}&0 创建一个线程,该线程将与ID比自己的ID高一个的线程进行同步,
  • ⎕TSYNC& 创建一个新线程,该线程将与先前的线程同步,并且获得一个比刚刚创建的线程高一个ID(假定没有其他线程正在创建线程)。
  • 导致无限循环(因此我们继续创建线程,直到出现死锁)。

一旦创建了第二个线程,这将在第一个线程开始运行之前立即死锁,这很快就会发生:

9:DEADLOCK

使用以下⎕TSYNC 0'. 命令保存2个字节:“ TID”为0
亚当

8

快去42

package main
func main(){<-make(chan int)}

不好意思,抱歉,没有提供其工作方式。这将创建一个int匿名通道并从中读取数据。这会暂停主线程,直到一个值沿通道发送为止,这显然不会发生,因为没有其他线程处于活动状态,因此出现了死锁。


2
它是如何工作的?
贾斯汀

4

Ruby,39个字符

T=Thread;t=T.current;T.new{t.join}.join

Johannes Kuhn的Java答案中偷偷地使用交叉联接的想法。

如果我们将代码调整到特定的环境,我们可以减少四个字符(到35)。JRuby的控制台IRB是单线程的:

T=Thread;T.new{T.list[0].join}.join


这是我以前的解决方案:

使线程卡在互斥锁上很容易:

m=Mutex.new;2.times{Thread.new{m.lock}}

但这不是一个适当的死锁,因为从技术上讲第二个线程不等待第一个线程。根据Wikipedia的说法,“保持并等待”是发生僵局的必要条件。第一个线程不等待,第二个线程不保留任何内容。

红宝石, 97 95个字符

m,n=Mutex.new,Mutex.new
2.times{o,p=(m,n=n,m)
Thread.new{loop{o.synchronize{p.synchronize{}}}}}

这是经典的僵局。两个线程争用两个资源,如果成功,则重试。通常,它们会在一秒钟内卡在我的计算机上。

但是,如果有无限多个线程(没有一个线程会无限消耗CPU且其中一些死锁),

红宝石, 87 85个字符

m,n=Mutex.new,Mutex.new
loop{o,p=(m,n=n,m)
Thread.new{o.synchronize{p.synchronize{}}}}

根据我的测试,它在线程数达到约4700后失败。希望它不会失败,直到每个线程都有机会运行(从而死锁或完成并释放新线程的空间)。根据我的测试,失败后线程数不会减少,这意味着在测试期间确实发生了死锁。此外,IRB在测试后死亡。


为什么你需要额外的op变量?你不能只是通过mn新主题?
Johannes Kuhn

@JohannesKuhn mn是全球性的。两个线程以相同的顺序看到它们。op是线程局部的(范围限于循环迭代)。使用t[...]可能会变得昂贵,而且我看不出有比通过闭包更好的将参数传递给线程的方法。添加额外的参数new将代码延长两个字符。
John Dvorak

@JohannesKuhn我希望你不要介意我借用了你的一些逻辑
John Dvorak

我不介意 做得好。
Johannes Kuhn

如果我们假设自己在主线程中,则可以使用以下方法将其减少到32个字符T=Thread;T.new{T.main.join}.join
histocrat


4

Bash + GNU coreutils,11个字节

mkfifo x;<x

x在当前目录中创建一个杂散FIFO (因此您不需要该名称的文件)。FIFO的删除方式与常规文件相同,因此不难清除。

FIFO具有写侧和读侧;尝试打开一个块,直到另一个进程打开另一个块,这似乎是有意设计为同步原语的。鉴于这里只有一个线程,因此只要尝试使用打开它<x,我们就会陷入困境。(您可以通过从另一个进程写入有问题的FIFO来解除僵局。)

这是一种与死锁不同的死锁,当有两个资源时,两个线程各有一个而又需要另一个;相反,在这种情况下,资源为零,而流程需要一个资源。基于其他答案,我认为这很重要,但是我可以理解僵局的纯粹主义者可能想拒绝该答案。

考虑一下,我实际上可以想到三种类似死锁的情况:

  1. “传统”死锁:两个线程各自等待释放的锁,该锁由另一个线程持有。

  2. 单个线程正在等待释放锁,但是它持有锁本身(因此阻止了自己释放锁)。

  3. 单个线程正在等待同步原语的释放,但是同步原语以自然锁定的状态启动,并且必须在外部解锁,并且没有任何程序可以做到这一点。

这是类型3的死锁,在某种程度上与其他两个类型根本不同:理论上,您可以编写一个程序来解锁所涉及的同步原语,然后运行它。就是说,同样的情况也适用于类型1和2的死锁,因为许多语言都允许您释放您不拥有的锁(如果有理由,您不应该这样做,也没有理由这样做。首先使用锁,但是可以使用…)。另外,值得考虑一个类似mkfifo x;<x;echo test>x; 该程序类似于2类死锁(它试图打开FIFO的两端,但是直到打开另一端之后才能打开一端),但是它是通过添加额外的内容而制成的永远不会在此之后运行的代码!我想这个问题是锁是否死锁或不依赖于打算使用锁的背后,所以很难客观地定义(尤其是在这样的情况下锁的唯一目的就是故意产生死锁)。



2

带有glibc的Bash,6字节

抱歉,恢复了旧线程,但无法抗拒。

作为根:

pldd 1

来自man pldd

BUGS
由于glibc的2.19,PLDD已破坏:它只是挂起执行时。尚不清楚它是否会被修复。


只要原来的踏板对时间不敏感,在旧踏板上接听就没有问题。
Ad Hoc Garf Hunter's

2

爪哇191

class B extends Thread{public static void main(String[]a)throws Exception{new B().join();}Thread d;B(){d=Thread.currentThread();start();}public void run(){try{d.join();}catch(Exception e){}}}

取消高尔夫:

class B extends Thread {
    Thread d;
    public static void main(String[] args) throws Exception {
        new B().join();
    }
    B() { // constructor
        d = Thread.currentThread();
        start();
    }
    public void run() {
        try {
            d.join();
        } catch (Exception e) {
        }
    }
}

启动一个新线程并join在其上(等待此线程完成),而新线程对原始线程执行相同的操作。


您能通过投掷Error代替而不是使它变短Exception吗?
mbomb007 '18

不。Thread.join()引发InteruptedException,而不是的子类Error
Johannes Kuhn '18

2

Tcl,76

package r Thread;thread::send [thread::create] "thread::send [thread::id] a"

僵局。

这将创建一个新线程,并告诉另一个线程向我的线程发送一条消息(要执行的脚本)。

但是向其他线程发送消息通常会阻塞,直到脚本执行完毕。而且,当它阻塞时,不会处理任何消息,因此两个线程都等待另一个线程处理它们的消息。


这是如何运作的?
John Dvorak

thread::send将应在其他线程中执行的脚本排队,并等待其完成。所以在最后,我们有线程1个正在等待线程2和线程2等待线程1
约翰内斯·库恩

1

带有监控滥用的替代Java(248个字符)

class A{public static void main(String args[]) throws Exception{final String a="",b="";new Thread(new Runnable(){public void run(){try {synchronized(b){b.wait();}} catch (Exception e) {}a.notify();}}).start();synchronized(a){a.wait();}b.notify();}}

1

Scala,104个字节

class A{s=>lazy val x={val t=new Thread{override def run{s.synchronized{}}};t.start;t.join;1}};new A().x

惰性val初始化块将挂起,直到满足条件为止。只能通过读取lazy val x的值来满足此条件-另一个应该完成此条件的线程不能这样做。因此,形成了循环依赖性,并且无法初始化惰性值。


这是如何运作的?
艾迪生·克伦普

我加了一个解释。
马丁·塞勒

1

Kotlin,35/37/55字节

一般主题:Thread.currentThread().join()

不包括JVM错误/针对此提交的非常专业的代码,由于当前执行线程现在被禁用,等待其自身死亡,因此该消息永远不会返回。


邪恶属性:35个字节(非竞争):35个字节

val a=Thread.currentThread().join()

将其放置在任何有效的属性声明中都会使初始化它的人陷入僵局。对于顶级属性,这将是类加载器初始化该文件的映射JVM类(默认情况下[file name]Kt.class)。

不竞争是因为“将其放置在任何地方都作为顶级属性”是限制性的。


功能:37字节

fun a()=Thread.currentThread().join()


main():55个字节

fun main(a:Array<String>)=Thread.currentThread().join()


1

PowerShell,36 28 23字节

gps|%{$_.waitforexit()}

自锁。我们获得了所有流程,Get-Process然后耐心等待每个流程退出...由于流程将自己等待,因此几乎不会发生。

编辑-感谢RomanGräf,节省了5个字节


(gps)|%{$_.waitforexit()}是短三个字节,并等待所有进程退出。
罗曼·格拉夫(RomanGräf),

@RomanGräf确实,但是gps在这种情况下不需要括号,因此总共节省了5个字节。
AdmBorkBork,2016年

0

C(仅Linux),31字节- 在线试用!

main(a){syscall(240,&a,0,a,0);}

系统调用240(0xf0)是futex(2)或快速用户空间互斥体。文档指出,第一个参数是指向futex的指针,第二个参数是操作(0表示FUTEX_WAIT,也就是说,等到另一个线程将futex解锁)。第三个参数是您希望futex仍处于锁定状态时所具有的值,第四个参数是指向超时的指针(没有超时则为NULL)。

显然,由于没有其他线程来解锁futex,因此它将在自我造成的死锁中永远等待。可以验证(通过top或其他任务管理器)该进程没有使用CPU时间,这是我们应该从死锁线程中得到的期望。


0

朱莉娅0.6,13字节

语言比问题新。等待未计划运行的任务(例如go例程,当前将在同一线程上,在Julia的未来版本中可能在另一个线程上)。

wait(@task +)

在线尝试!


0

BotEngine,3x3 = 9(9个字节)

v
lCv
> <

第5步以两个机器人无限期地等待彼此移动结束。一个机器人无法移动,因为它正在等待另一个机器人移出右下角的方块,而另一个机器人则无法移动,因为它正在等待第一个机器人移出底部中心方块。


0

瀑布模型(比率下降),13个字节

[[2,1],[1,1]]

在线尝试!

这是一个有趣的现成答案。这是瀑布模型中最简单的无限循环(瀑布模型中的变量在没有其他任何情况发生时会重复递减;该程序定义了一个变量,每当它递减时就会递增,因此不可能发生任何事情)。

这个问题要求死锁,而不是无限循环。但是,我们可以利用特定实现的行为。在优化级别2或更高级别(默认值为3)时,解释器Ratiofall将检测到无限循环并对其进行优化……陷入僵局!因此,至少如果我们认为语言是由其实现定义的(在此站点上通常如此),则该程序确实确实定义了死锁,而不是无限循环。

您可以在“在线试用”的时间报告中看到僵局的证据!上面的链接:

Real time: 60.004 s
User time: 0.006 s
Sys. time: 0.003 s
CPU share: 0.01 %
Exit code: 124

该程序运行了60秒(直到TIO自动终止它),但是在大部分时间里,CPU使用率没有,程序运行没有时间,内核也没有时间代表程序。

为了获得更充分的证据,可以在系统调用级别的调试器(例如)下运行Ratiofall strace。在Linux上执行此操作将显示解释器在futex试图获取永远不会释放的锁的系统调用上阻塞。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.