Bir Kilitlenme Yaratan En Kısa Kod


11

Bir kilitlenme oluşturmak için en kısa kodu yazın . Kod yürütme işlemi durdurulmalıdır, bu nedenle çalışmaz:

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

Kodun kilitlenmeye gittiğinden emin olmak zorunda değilsiniz , neredeyse kesin olarak (sonsuz süre için çalışırsanız, kilitlenecektir).


Aynı iş parçacığını aynı iş parçacığından iki kez kilitlemeye çalışırsam kilitlenme sayılır mı? (üzgünüm bu soruyu kum havuzunda anlamadım)
John Dvorak

@JanDvorak Bir iş parçacığı elde edemediği bir şey beklediğinden (başka bir iş parçacığı tutuyor ve ilk iş parçacığının ne beklediğinden dolayı) kod yürütmenin durduğu bir durum oluşturuyor mu? Yoksa tek bir iplik mi? Bir iş parçacığında böyle bir durum yaratabiliyorsanız, sorun yoktur. Kilitlenmeyle ilgili wikepedia makalesini okuyun, beklediğim bu.
Justin

2
Code execution must haltAnlamıyorum. Durursa nasıl bir çıkmaza girer? Yani sadece bir pislik gibi dönmekten ziyade bir şey bekleyecek mi?
Cruncher

@Cruncher Çalışmayan örneğe bir göz atın. Döngü çalışmaya devam ettiği için kod yürütmesi durmuyor. Evet, çevirmek yerine beklemek demek istiyorum.
Justin

Eğer kullanıcı arayüzü iş parçacığını Dyalog APL'de bekletirseniz, bu bir javascript yanıtı almanın bir umut olduğu anlamına mı geliyor? Her ne kadar bu tür bir düşüncenin sadece bu cevabın kapısını açacağından şüphelenmeme rağmen: Javascript 6 "wait ()" ... ermmm. Hayır. İlgili: Bir iş parçacığının Deadlock'un kendisi olması mümkün müdür?
Nathan Cooper

Yanıtlar:


11

Dyalog APL (10)

⎕TSYNC⎕TID

⎕TSYNCverilen iplik bitinceye kadar ipliğin beklemesini ⎕TIDsağlar, geçerli ipliği verir.

Dyalog APL kilitlenmeleri tanıyabilir, bu yüzden hemen yanıt verir

DEADLOCK

Eğlenceli olan şey, herhangi bir ekstra ipliği yumurtlamanıza bile gerek yok, UI iş parçacığının kendisini bekletmesi bol.

Bu hile yapıyorsa ve aslında yeni iş parçacıkları gerekiyorsa, bunu 27 karakterle yapabilirsiniz:

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

F & xFdeğer üzerinde yeni bir iş parçacığında çalışır xve iş parçacığı kimliğini döndürür. Yani:

  • {⎕TSYNC⎕TID+1}&0 ID'si kendisinden yüksek olan bir evre ile senkronize olacak bir evre yaratır,
  • ⎕TSYNC& önceki iş parçacığıyla senkronize edilecek ve yeni oluşturulan iş parçacığından bir tane daha yüksek bir kimlik alan yeni bir iş parçacığı oluşturur (başka bir iş parçacığı oluşturmadığı varsayılarak).
  • Sonsuz bir döngüye neden olur (bu yüzden bir kilitlenme olana kadar dişler oluşturmaya devam ederiz).

Bu, ilk iş parçacığı çalışmaya başlamadan önce ikinci iş parçacığı oluşturulur oluşturulmaz kilitlenecektir:

9:DEADLOCK

⎕TSYNC 0'. Şununla 2 bayt kaydedin: ⎕TID` 0.
Adam

8

Git, 42

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

Özür, inatçı, nasıl çalıştığını sağlamadığı için. Bu, anonim bir ints kanalı oluşturur ve ondan okur. Bu, kanaldan bir değer gönderilinceye kadar ana iş parçacığını duraklatır.


2
O nasıl çalışır?
Justin

4

Ruby, 39 karakter

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

Johannes Kuhn'un Java cevabından utanmadan çalınan çapraz birleştirme kullanma fikri .

Kodu belirli bir ortama ayarlarsak dört karakteri tıraş edebiliriz (en fazla 35 karakter ). JRuby'nin IRB konsolu tek iş parçacıklı:

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


Bu benim önceki çözümüm:

bir muteks sıkışmış bir iplik almak kolaydır:

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

ancak bu uygun bir kilitlenme değildir, çünkü ikinci iş parçacığı teknik olarak ilk iş parçacığını beklemez. "bekle ve bekle" Wikipedia'ya göre bir kilitlenme için gerekli bir koşuldur. İlk iş parçacığı beklemiyor ve ikinci iş parçacığı hiçbir şey tutmuyor.

Yakut, 97 95 karakter

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

bu klasik bir kilitlenme. İki iş parçacığı iki kaynak için yarışır, başarılı olursa yeniden dener. Normalde makineme bir saniye içinde takılırlar.

Ancak, sonsuz sayıda iş parçacığı (hiçbiri CPU'yu sonsuz olarak tüketmeyen ve bazıları kilitlenmeyen) varsa,

Yakut, 87 85 karakter

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

Testime göre, iplik sayısı yaklaşık 4700'e ulaştıktan sonra başarısız oluyor. Umarım her bir iş parçacığının çalışma şansı bulana kadar başarısız olmaz (böylece ya kilitlenme ya da bitirme ve yenisi için alan boşaltma). Testime göre, hata oluştuktan sonra iplik sayısı düşmez, yani test sırasında bir kilitlenme meydana gelir. Ayrıca, IRB testten sonra öldü.


niye ekstra gerekiyor ove pdeğişkenleri? Sadece geçmek mve nyeni Konu için değil mi ?
Johannes Kuhn

@JohannesKuhn mve nküreseldir. Her iki iş parçacığı da bunları aynı sırada görür. ove pevre yereldir (döngü yinelemesine dahil edilir). Kullanmak t[...]muhtemelen pahalı olurdu ve ben iplik kapatma parametreleri daha iyi bir yol göremiyorum. newKodu iki karakter uzatmak için fazladan parametreler eklemek .
John Dvorak

@JohannesKuhn Umarım mantığınızdan bazılarını ödünç aldım sakıncası yok
John Dvorak

Umurumda değil. Aferin.
Johannes Kuhn

Ana iplikte olduğumuzu varsayarsak, bunu 32 karaktere kadar tıraş edebilirizT=Thread;T.new{T.main.join}.join
histokrat


4

Bash + GNU coreutils, 11 bayt

mkfifo x;<x

xGeçerli dizinde kaçak bir FIFO oluşturur (bu nedenle bu adda bir dosyaya ihtiyacınız yoktur). FIFO'lar normal dosyalarla aynı şekilde silinebilir, bu nedenle temizlenmesi zor olmamalıdır.

Bir FIFO'nun bir yazma tarafı ve bir okuma tarafı vardır; başka bir işlem diğerini açana kadar bir blok açmaya çalışıyor ve bu kasıtlı olarak bir senkronizasyon ilkesi olarak tasarlanmış gibi görünüyor. Burada sadece bir iplik olduğu göz önüne alındığında, açmaya çalıştığımız anda <xsıkışıp kalıyoruz. (Kilitlenmeyi başka bir işlemden söz konusu FIFO'ya yazarak açabilirsiniz.)

Bu, iki kaynak olduğunda birinden farklı bir çıkmazdır ve iki iş parçasının her birinin bir tane vardır ve diğerine ihtiyacı vardır; daha ziyade, bu durumda, sıfır kaynak vardır ve sürecin bir kaynağa ihtiyacı vardır. Diğer cevaplara dayanarak, bunun önemli olduğunu düşünüyorum, ancak bir kilitlenme puristinin cevaba nasıl izin vermemek isteyebileceğini anlayabiliyorum.

Düşünmeye gel, aslında üç çıkmaza benzer durumu düşünebilirim:

  1. "Geleneksel" kilitlenme: iki iş parçacığı, diğer iş parçacığı tarafından tutulan bir kilidin serbest bırakılmasını bekliyor.

  2. Tek bir iş parçacığı bir kilidin serbest bırakılmasını bekler, ancak kilidin kendisini tutar (ve böylece kendisini serbest bırakmasını engeller).

  3. Tek bir iş parçacığı, bir eşitleme ilkesinin serbest bırakılmasını bekler, ancak eşitleme ilkesi doğal olarak kilitli durumda başlar ve dışarıdan kilidinin açılması gerekir ve bunu yapacak hiçbir şey programlanmamıştır.

Bu, diğer ikisinden temel olarak farklı olan bir tip 3 çıkmazıdır: teorik olarak, söz konusu senkronizasyon ilkelinin kilidini açmak için bir program yazabilir, sonra çalıştırabilirsiniz. Bununla birlikte, birçok dilin sahip olmadığınız bir kilidi açmanıza izin verdiği göz önüne alındığında, aynı durum tip 1 ve 2'deki kilitlenmeler için de geçerlidir ( eğer bir nedeniniz varsa bunu yapmanız gerekmiyor ve bunu yapmak için hiçbir nedeniniz yok) kilitleri ilk etapta kullanın, ama işe yarıyor…). Ayrıca, aşağıdaki gibi bir program düşünmeye değermkfifo x;<x;echo test>x; bu program, tip 2 çıkmazın tersi (FIFO'nun her iki ucunu da açmaya çalışıyor, ancak diğer ucu açana kadar bir ucu açamıyor), ancak ekstra ekleyerek bu programdan yapıldı Bundan sonra asla çalışmayan kod! Sanırım sorun, bir kilidin kilitlenip kilitlenmediği ya niyete bağlı kilidin kullanımının arkasında, bu nedenle nesnel olarak tanımlamak zordur (özellikle kilidin tek amacının kasıtlı olarak bir kilitlenme üretmek olduğu durumlarda).


2

C #, 100

class C{static C(){var t=new System.Threading.Thread(Main);t.Start();t.Join();}static void Main(){}}

Bkz: Kilitsiz kilitlenme


1
Eşyaları konumundan ' static Ce taşıyamaz Mainmısınız?
Roman Gräf

2

Glibc ile bas, 6 Bayt

Eski bir diziyi canlandırdığım için özür dilerim, ama direnemedim

Kök olarak:

pldd 1

Gönderen adam PLDD :

BÖCEK
glibc'nin 2.19 beri PLDD bozuldu: sadece kilitleniyor yürütüldüğünde. Sabitlenip sabitlenmeyeceği belli değil.


Orijinalin zamana duyarlı olmadığı sürece eski bir lastik sırtına cevap vermede sorun yoktur.
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){}}}

Ungolfed:

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

Yeni bir iş parçacığı başlatır ve joinüzerinde (bu iş parçacığı bitene kadar bekleyin), yeni iş parçacığı orijinal iş parçacığıyla aynı şeyi yapar.


Atmak ve yakalamak Erroryerine kısaltabilir Exceptionmisin?
mbomb007

Hayır! bir alt sınıfı olmayan bir Thread.join()atar . InteruptedExceptionError
Johannes Kuhn

2

Tcl, 76

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

Kilitlenme.

Bu yeni bir iş parçacığı oluşturur ve diğer iş parçacığına benim iş parçacığı bir ileti (yürütmek için komut dosyası) göndermek için söyler.

Ancak başka bir iş parçacığına mesaj göndermek genellikle komut dosyası yürütülene kadar engeller. Engelleme sırasında hiçbir ileti işlenmez, bu nedenle her iki Konu da diğerinin iletilerini işlemesini bekler.


bu nasıl çalışıyor?
John Dvorak

thread::sendbaşka bir iş parçacığında yürütülmesi gereken bir komut dosyasını sıralar ve tamamlanmasını bekleyin. Sonunda, Konu 1'i Konu 2'yi ve Konu 2'yi Konu 1'i bekliyor.
Johannes Kuhn

1

monitör kötüye kullanımı ile alternatif java (248 chara)

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 bayt

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

Tembel val başlatma bloğu, bir koşul yerine getirilene kadar askıya alınır. Bu koşul yalnızca tembel val x değeri okunarak yerine getirilebilir - bu koşulu tamamlaması beklenen başka bir iş parçacığı bunu yapamaz. Böylece dairesel bir bağımlılık oluşur ve tembel val başlatılamaz.


Bu nasıl çalışıyor?
Addison Crump

Bir açıklama ekledim.
Martin Seeler

1

Kotlin, 35/37/55 bayt

Genel tema: Thread.currentThread().join() .

JVM hataları / bu gönderime karşı çok özel bir kod hariç tutulduğunda, mevcut yürütme iş parçacığı artık ölmesini beklerken devre dışı bırakıldığından bu shoud asla geri dönmez.


Kötü mülkiyet: 35 bayt (rakip olmayan): 35 bayt

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

Bunu bir özellik bildiriminin geçerli olduğu herhangi bir yere koymak, onu başlatan kişi kilitlenecektir. Üst düzey bir özellik söz konusu olduğunda, bu dosya için eşlenen JVM sınıfını başlatan (varsayılan olarak [file name]Kt.class) sınıf yükleyici olacaktır .

"Bunu herhangi bir yere üst düzey mülk olarak koy" nedeniyle kısıtlayıcı değildir.


İşlev: 37 bayt

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


ana (): 55 bayt

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


1

PowerShell, 36 28 23 bayt

gps|%{$_.waitforexit()}

Kendinden kilitlenme. Tüm süreçleri alıyoruz Get-Processve sonra her birinin çıkmasını sabırla bekliyoruz ... bu yaklaşık olarak asla olmayacak, çünkü süreç kendini bekliyor olacak.

Düzenle - Roman Gräf sayesinde 5 bayt kaydedildi


(gps)|%{$_.waitforexit()}üç bayt daha kısadır ve tüm işlemlerin çıkmasını bekler.
Roman Gräf

@ RomanGräf Gerçekten, ama gpsbu durumda parens gerekmez , bu yüzden toplam 5 bayt kurtardı.
AdmBorkBork

0

C (yalnızca Linux), 31 Bayt - Çevrimiçi deneyin!

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

Sistem çağrısı 240 (0xf0) futex (2) veya hızlı kullanıcı alanı . Belgeler, birinci argümanın futex için bir işaretçi olduğunu, ikinci argümanın işlem olduğunu belirtir (0, FUTEX_WAIT anlamına gelir, yani başka bir evre futeksin kilidini açana kadar bekleyin). Üçüncü argüman futex'in hala kilitliyken olmasını beklediğiniz değerdir ve dördüncüsü zaman aşımının göstergesidir (hiçbir zaman aşımı için NULL).

Açıkçası, futex kilidini açmak için başka bir iş parçacığı olmadığından, kendiliğinden oluşan çıkmazda sonsuza kadar bekleyecek. topKilitlenmemiş bir iş parçacığından beklediğimiz gibi, işlemin CPU zamanı kullanmadığı ( başka bir görev yöneticisi aracılığıyla) doğrulanabilir .


0

Julia 0.6 , 13 bayt

Sorudan daha yeni bir dil. Çalıştırılması planlanmayan bir görevi bekleyin (bir rutin gibi, şu anda aynı iş parçacığında olacak, Julia'nın gelecekteki sürümlerinde başka bir iş parçacığında olabilir).

wait(@task +)

Çevrimiçi deneyin!


0

BotEngine, 3x3 = 9 (9 bayt)

v
lCv
> <

Adım 5, birbirinin hareket etmesini bekleyen iki botla sona erer ... bir bot hareket edemez, çünkü diğeri sağ alt köşeden dışarı çıkmayı bekler ve diğer bot hareket edemez çünkü alt orta karenin dışına taşınan ilk bot.


0

Şelale Modeli (Ratiofall), 13 bayt

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

Çevrimiçi deneyin!

İşte eğlenceli bir cevap. Bu, Şelale Modeli'ndeki mümkün olan en basit sonsuz döngüdür (Şelale Modeli'ndeki değişkenler, başka bir şey olmadığında tekrar tekrar azalır; Bu program, azaldığında kendini artıran bir değişkeni tanımlar, böylece hiçbir şeyin gerçekleşemeyeceği bir yol yoktur).

Soru sonsuz bir döngü değil, bir kilitlenme ister. Ancak, belirli uygulamaların davranışlarından yararlanabiliriz. Optimizasyon seviyesi 2 veya üstünde (varsayılan değer 3), tercüman Oran Düşmesi sonsuz döngüyü algılar ve onu bir kilitlenmeye dönüştürür! Dolayısıyla, en azından dillerin uygulamaları tarafından tanımlandığını düşünürsek (genellikle bu sitede durum budur), bu program gerçekten sonsuz bir döngüden ziyade bir kilitlenme tanımlar.

Kilitlenmenin kanıtlarını Çevrimiçi Deneyin! Raporunun zaman raporundan görebilirsiniz. yukarıdaki bağlantı:

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

Program 60 saniye boyunca çalıştı (TIO otomatik olarak sona erene kadar), ancak çoğu zaman CPU kullanımı, program tarafından harcanan zaman ve çekirdek adına program adına harcanan zaman yoktu.

Daha da güçlü kanıtlar elde etmek için Ratiofall'ı sistem çağrısı düzeyinde bir hata ayıklayıcı altında strace; Linux'ta bunu yapmak, tercümanın futexasla serbest bırakılmayacak bir kilit almaya çalışan bir sistem çağrısında engellenmesini gösterecektir .


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.