Mã ngắn nhất tạo ra bế tắc


11

Viết mã ngắn nhất để tạo bế tắc . Việc thực thi mã phải tạm dừng, vì vậy điều này không hoạt động:

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){}}
}

Không cần chắc chắn rằng mã đi vào bế tắc , gần như chắc chắn (nếu bạn chạy trong thời gian vô hạn, nó sẽ bế tắc).


Có phải nó được tính là một bế tắc nếu tôi cố gắng khóa cùng một khóa (không reentrant) hai lần từ cùng một chủ đề? (xin lỗi tôi đã không nhận ra câu hỏi này trong hộp cát)
John Dvorak

@JanDvorak Điều đó có tạo ra một tình huống trong đó việc thực thi mã tạm dừng vì một luồng đang chờ một thứ gì đó không thể nhận được (vì một luồng khác đang giữ nó và chờ đợi chuỗi đầu tiên có gì)? Hay là một chủ đề? Nếu bạn có thể tạo một tình huống như vậy với một luồng, thì tốt thôi. Đọc bài viết về wikepedia về bế tắc, đó là những gì tôi mong đợi.
Justin

2
Code execution must haltTôi không hiểu Làm thế nào là một bế tắc nếu nó dừng lại? Bạn có nghĩa là nó sẽ chờ đợi một cái gì đó thay vì chỉ xoay tròn như lỗ đít?
Cruncher

@Cruncher Hãy xem ví dụ không hoạt động. Việc thực thi mã không dừng lại vì vòng lặp tiếp tục chạy. Vâng, tôi có nghĩa là chờ đợi hơn là quay cuồng.
Justin

Vì vậy, nếu bạn có thể làm cho luồng UI chờ chính nó trong Dyalog APL, điều đó có nghĩa là có hy vọng nhận được câu trả lời javascript không? Mặc dù tôi nghi ngờ kiểu suy nghĩ đó sẽ mở ra cánh cửa cho câu trả lời này: Javascript 6 "Wait ()" ... ermmm. Không. Liên quan: Có thể cho một chủ đề để Deadlock chính nó?
Nathan Cooper

Câu trả lời:


11

Thuốc nhuộm APL (10)

⎕TSYNC⎕TID

⎕TSYNClàm cho luồng chờ cho đến khi luồng đã cho kết thúc, ⎕TIDđưa ra luồng hiện tại.

Dyalog APL có thể nhận ra sự bế tắc, vì vậy nó ngay lập tức đáp ứng với

DEADLOCK

Điều thú vị là, bạn thậm chí không cần phải sinh ra bất kỳ chủ đề bổ sung nào, làm cho chủ đề UI chờ đợi là rất nhiều.

Nếu điều này là gian lận và các chủ đề mới thực sự cần thiết, bạn có thể thực hiện trong 27 ký tự:

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

F & xchạy Ftrong một luồng mới trên giá trị xvà trả về ID luồng. Vì thế:

  • {⎕TSYNC⎕TID+1}&0 tạo một luồng sẽ đồng bộ hóa với luồng có ID cao hơn luồng của nó,
  • ⎕TSYNC& tạo ra một luồng mới sẽ đồng bộ hóa với luồng trước đó và có một ID cao hơn luồng được tạo (giả sử không có gì khác đang tạo luồng).
  • gây ra một vòng lặp vô hạn (vì vậy chúng tôi tiếp tục tạo chủ đề cho đến khi có bế tắc).

Điều này sẽ bế tắc ngay khi luồng thứ hai được tạo trước khi chuỗi đầu tiên bắt đầu chạy, khá sớm:

9:DEADLOCK

Lưu 2 byte với: ⎕TSYNC 0'. ⎕TID` là 0.
Adám

8

Đi, 42

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

Xin lỗi, downvoter, vì không cung cấp cách thức hoạt động. Điều này tạo ra một kênh ẩn danh của ints và đọc từ nó. Điều này tạm dừng luồng chính cho đến khi một giá trị được gửi xuống kênh, điều này rõ ràng không bao giờ xảy ra do không có luồng nào khác đang hoạt động, và do đó, bế tắc.


2
Làm thế nào nó hoạt động?
Justin

4

Ruby, 39 ký tự

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

Ý tưởng sử dụng một sự tham gia chéo đáng xấu hổ bị đánh cắp từ câu trả lời Java của Johannes Kuhn .

Chúng tôi có thể tắt bốn ký tự (đến 35 ) nếu chúng tôi điều chỉnh mã theo một môi trường cụ thể. Bảng điều khiển IRB của JRuby là một luồng:

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


Đây là giải pháp trước đây của tôi:

nhận được một chủ đề bị mắc kẹt trên một mutex là dễ dàng:

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

nhưng đây không phải là một bế tắc thích hợp, bởi vì luồng thứ hai về mặt kỹ thuật không chờ đợi luồng đầu tiên. "Giữ và chờ" là một điều kiện cần thiết cho sự bế tắc theo Wikipedia. Chuỗi đầu tiên không chờ đợi và luồng thứ hai không giữ bất cứ điều gì.

Hồng ngọc 97 95 ký tự

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

đây là một bế tắc cổ điển. Hai luồng cạnh tranh cho hai tài nguyên, thử lại nếu họ thành công. Thông thường họ bị kẹt trong vòng một giây trên máy của tôi.

Nhưng, nếu có vô số luồng (không có luồng nào tiêu thụ CPU vô hạn và một số trong số đó là bế tắc) thì OK,

Hồng ngọc 87 85 ký tự

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

Theo thử nghiệm của tôi, nó thất bại sau khi số luồng đạt khoảng 4700. Hy vọng rằng nó không thất bại cho đến khi mỗi luồng có cơ hội chạy (do đó là bế tắc hoặc hoàn thiện và giải phóng không gian cho một cái mới). Theo thử nghiệm của tôi, số lượng luồng không giảm sau khi xảy ra lỗi, có nghĩa là sự bế tắc đã xảy ra trong quá trình thử nghiệm. Ngoài ra, IRB đã chết sau khi thử nghiệm.


Tại sao bạn cần thêm o& pbiến? Bạn có thể vượt qua mncho chủ đề mới?
Julian Kuhn

@JohannesKuhn mnlà toàn cầu. Cả hai chủ đề nhìn thấy chúng theo cùng một thứ tự. oplà luồng cục bộ (nằm trong phạm vi vòng lặp). Việc sử dụng t[...]có thể sẽ tốn kém và tôi không thể thấy cách nào tốt hơn để truyền tham số cho luồng hơn là đóng. Thêm các tham số bổ sung để newkéo dài mã bằng hai ký tự.
John Dvorak

@JohannesKuhn Tôi hy vọng bạn không phiền tôi đã mượn một số logic của bạn
John Dvorak

Tôi không phiền Làm tốt lắm.
Julian Kuhn

Nếu chúng ta giả sử chúng ta đang ở trong chủ đề chính, chúng ta có thể cạo nó xuống còn 32 ký tự vớiT=Thread;T.new{T.main.join}.join
histocrat


4

Lõi lõi Bash + GNU, 11 byte

mkfifo x;<x

Tạo một FIFO đi lạc xtrong thư mục hiện tại (vì vậy bạn sẽ không cần phải có tệp theo tên đó). Các bộ xếp hình có thể bị xóa giống như các tệp thông thường, vì vậy không khó để xóa.

Một FIFO có một mặt viết và một mặt đọc; cố gắng mở một khối cho đến khi một quá trình khác mở ra khối khác và dường như nó được thiết kế có chủ đích như một nguyên thủy đồng bộ hóa. Vì chỉ có một luồng ở đây, ngay khi chúng tôi cố gắng mở nó <x, chúng tôi bị kẹt. (Bạn có thể hủy bỏ bế tắc thông qua việc viết thư cho FIFO trong câu hỏi từ quy trình khác.)

Đây là một loại bế tắc khác với một khi có hai tài nguyên và hai luồng mỗi cái có một và cần cái khác; thay vào đó, trong trường hợp này, không có tài nguyên và quá trình cần một tài nguyên. Dựa trên các câu trả lời khác, tôi nghĩ rằng điều này có giá trị, nhưng tôi có thể hiểu làm thế nào một người theo chủ nghĩa bế tắc có thể muốn không cho phép câu trả lời.

Nghĩ về nó, tôi thực sự có thể nghĩ về ba tình huống giống như bế tắc:

  1. Bế tắc "truyền thống": hai luồng đang chờ một khóa được phát hành, được giữ bởi luồng khác.

  2. Một chủ đề duy nhất đang chờ khóa được phát hành, nhưng nó giữ chính khóa đó (và do đó tự ngăn chặn không thể giải phóng nó).

  3. Một luồng duy nhất đang chờ một nguyên thủy đồng bộ hóa được phát hành, nhưng nguyên thủy đồng bộ hóa bắt đầu ở trạng thái khóa tự nhiên và phải được mở khóa bên ngoài và không có gì được lập trình để làm điều đó.

Đây là một bế tắc của loại 3, về cơ bản khác với hai loại kia: về lý thuyết, bạn có thể viết một chương trình để mở khóa nguyên thủy đồng bộ hóa trong câu hỏi, sau đó chạy nó. Điều đó nói rằng, điều tương tự cũng áp dụng cho các bế tắc loại 1 và 2, với điều kiện là nhiều ngôn ngữ cho phép bạn giải phóng một khóa mà bạn không sở hữu (bạn không được phép và sẽ không có lý do gì để làm như vậy nếu bạn có lý do để sử dụng ổ khóa ở vị trí đầu tiên, nhưng nó hoạt động được). Ngoài ra, đáng để xem xét một chương trình nhưmkfifo x;<x;echo test>x; chương trình đó trái ngược với bế tắc loại 2 (nó đang cố mở cả hai đầu của FIFO, nhưng nó không thể mở một đầu cho đến khi nó mở đầu kia), nhưng nó được tạo ra từ đầu này bằng cách thêm vào mã không bao giờ chạy sau cái này! Tôi đoán vấn đề là khóa có bị bế tắc hay không phụ thuộc vào ý định đằng sau việc sử dụng khóa, vì vậy thật khó để xác định một cách khách quan (đặc biệt trong trường hợp như thế này khi mục đích duy nhất của khóa là tạo ra sự bế tắc có chủ ý ).



2

Bash với glibc, 6 byte

Xin lỗi để hồi sinh một chủ đề cũ, nhưng không thể cưỡng lại.

Là gốc:

pldd 1

Từ người đàn ông pldd :

LỖI
Kể từ glibc 2.19, pldd được chia: nó chỉ bị treo khi thực hiện. Không rõ liệu nó sẽ được sửa chữa.


Không có vấn đề gì với việc trả lời trên một guồng cũ miễn là bản gốc không nhạy cảm với thời gian.
Ad Hoc Garf Hunter

2

Java, 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){}}}

Ung dung:

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) {
        }
    }
}

Bắt đầu một Chủ đề mới và jointrên đó (đợi cho đến khi chủ đề này kết thúc), trong khi Chủ đề mới thực hiện tương tự với Chủ đề ban đầu.


Bạn có thể làm cho nó ngắn hơn bằng cách ném và bắt Errorthay vì Exception?
mbomb007

Không. Thread.join()ném một InteruptedException, mà không phải là một lớp con của Error.
Julian Kuhn

2

Tcl, 76

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

Bế tắc.

Điều này tạo ra một Chủ đề mới và báo cho chủ đề khác gửi thông điệp của tôi (tập lệnh để thực thi).

Nhưng việc gửi tin nhắn đến một Chủ đề khác thường chặn cho đến khi tập lệnh được thực thi. Và trong khi nó đang chặn, không có tin nhắn nào được xử lý, vì vậy cả hai Chủ đề đều chờ người khác xử lý tin nhắn của họ.


Cái này hoạt động ra sao?
John Dvorak

thread::sendxếp hàng một kịch bản nên được thực thi trong một luồng khác và chờ cho nó hoàn thành. Vì vậy, cuối cùng, chúng ta có Chủ đề 1 đang chờ Chủ đề 2 và Chủ đề 2 đang chờ Chủ đề 1.
Johannes Kuhn

1

java thay thế với lạm dụng màn hình (248 charas)

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 byte

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

Khối khởi tạo val lười biếng tạm dừng cho đến khi một điều kiện được đáp ứng. Điều kiện này chỉ có thể được đáp ứng bằng cách đọc giá trị của Lazy val x - một luồng khác được cho là hoàn thành điều kiện này không thể thực hiện được. Do đó, một phụ thuộc vòng tròn được hình thành và val lười biếng không thể được khởi tạo.


Cái này hoạt động ra sao?
Addison Crump

Tôi đã thêm một lời giải thích.
Martin Seeler

1

Kotlin, 35/37/55 byte

Chủ đề chung : Thread.currentThread().join().

Không bao gồm các lỗi JVM / mã rất chuyên biệt chống lại đệ trình này, điều này sẽ không bao giờ quay trở lại vì luồng thực thi hiện tại bị vô hiệu hóa đang chờ chính nó chết.


Thuộc tính xấu: 35 byte (không cạnh tranh): 35 byte

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

Đặt điều này bất cứ nơi nào một tuyên bố tài sản là hợp lệ sẽ bế tắc bất cứ ai đang khởi tạo nó. Trong trường hợp thuộc tính cấp cao nhất, đó sẽ là trình nạp lớp khởi tạo lớp JVM được ánh xạ cho tệp đó (theo mặc định [file name]Kt.class).

Không cạnh tranh vì "đặt điều này như một tài sản cấp cao nhất ở bất cứ đâu" là hạn chế.


Chức năng: 37 byte

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


chính (): 55 byte

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


1

PowerShell, 36 28 23 byte

gps|%{$_.waitforexit()}

Tự bế tắc. Chúng tôi nhận được tất cả các quy trình Get-Processvà sau đó kiên nhẫn chờ đợi từng quy trình thoát ra ... điều này sẽ xảy ra gần như không bao giờ, vì quy trình sẽ tự chờ đợi.

Chỉnh sửa - Đã lưu 5 byte nhờ Roman Gräf


(gps)|%{$_.waitforexit()}ngắn hơn ba byte và chờ tất cả các quá trình thoát ra.
Roman Gräf

@ RomanGräf Thật vậy, nhưng không cần các parens xung quanh gpstrong trường hợp đó, vì vậy đã lưu tổng cộng 5 byte.
admBorkBork

0

C (chỉ dành cho Linux), 31 byte - Hãy thử trực tuyến!

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

Cuộc gọi hệ thống 240 ( 0xf0 ) là futex (2) hoặc mutex không gian người dùng nhanh. Tài liệu nói rằng đối số đầu tiên là một con trỏ tới futex, đối số thứ hai là hoạt động (0 có nghĩa là FUTEX_WAIT, nghĩa là đợi cho đến khi một luồng khác mở khóa Futex). Đối số thứ ba là giá trị mà bạn mong đợi Futex sẽ có trong khi vẫn bị khóa và thứ tư là con trỏ đến thời gian chờ (NULL không có thời gian chờ).

Rõ ràng, vì không có chủ đề nào khác để mở khóa Futex, nó sẽ chờ mãi trong bế tắc tự gây ra. Có thể xác minh (thông qua tophoặc trình quản lý tác vụ khác) rằng quy trình không sử dụng thời gian CPU, như chúng ta mong đợi từ một luồng bị khóa.


0

Julia 0,6 , 13 byte

Ngôn ngữ mới hơn câu hỏi. Đợi một nhiệm vụ (như thói quen đi, hiện sẽ nằm trên cùng một chuỗi, trong các phiên bản tương lai của Julia, nó có thể nằm trên một luồng khác) không được lên lịch để chạy.

wait(@task +)

Hãy thử trực tuyến!


0

BotEngine, 3x3 = 9 (9 byte)

v
lCv
> <

Bước 5 kết thúc với hai bot chờ nhau di chuyển vô thời hạn ... một bot không thể di chuyển vì nó chờ người kia di chuyển ra khỏi ô vuông bên phải và bot còn lại không thể di chuyển vì nó đang chờ bot đầu tiên để di chuyển ra khỏi quảng trường trung tâm dưới cùng.


0

Mô hình thác nước (Ratiofall), 13 byte

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

Hãy thử trực tuyến!

Đây là một câu trả lời thú vị. Đây là vòng lặp vô hạn đơn giản nhất có thể có trong Mô hình thác nước (các biến trong Mô hình thác nước lặp đi lặp lại bất cứ khi nào không có gì khác xảy ra; chương trình này xác định một biến tăng lên bất cứ khi nào nó giảm, vì vậy không có cách nào có thể xảy ra).

Câu hỏi yêu cầu sự bế tắc, không phải là một vòng lặp vô hạn. Tuy nhiên, chúng ta có thể khai thác hành vi của việc thực hiện cụ thể. Ở mức tối ưu hóa cấp 2 trở lên (mặc định là 3), trình thông dịch Ratiofall sẽ phát hiện vòng lặp vô hạn và tối ưu hóa nó thành một bế tắc! Vì vậy, ít nhất nếu chúng ta coi các ngôn ngữ được xác định bởi việc triển khai chúng (thường là trường hợp trên trang web này), chương trình này thực sự xác định một bế tắc, thay vì một vòng lặp vô hạn.

Bạn có thể thấy bằng chứng về sự bế tắc từ báo cáo thời gian trên Dùng thử trực tuyến! liên kết ở trên:

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

Chương trình đã chạy trong 60 giây (cho đến khi TIO tự động chấm dứt nó), nhưng trong phần lớn thời gian đó, không có việc sử dụng CPU, không có thời gian dành cho chương trình đang chạy và không dành thời gian cho kernel thay mặt cho chương trình.

Để có được bằng chứng mạnh mẽ hơn nữa, bạn có thể chạy Ratiofall theo trình gỡ lỗi cấp gọi hệ thống như strace; thực hiện điều này trên Linux sẽ hiển thị trình thông dịch chặn trên một futexcuộc gọi hệ thống cố gắng thực hiện khóa mà sẽ không bao giờ được phát hành.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.