Найкоротший код, який створює тупик


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

Не потрібно бути впевненим, що код переходить у глухий кут , майже майже напевно (якщо ви запустите нескінченний час, він зайде в тупик).


Чи вважається це глухим кутом, якщо я спробую двічі заблокувати той самий (невідповідальний) замок із однієї теми? (вибачте, що я не усвідомив це питання в пісочниці)
Джон Дворак

@JanDvorak Чи це створює ситуацію, коли виконання коду зупиняється, тому що один потік чекає чогось не може отримати (тому що інший тримає його і чекає того, що має перший потік)? Або це одна нитка? Якщо ви можете створити таку ситуацію однією ниткою, то це добре. Прочитайте статтю wikepedia про тупик, саме цього я очікую.
Джастін

2
Code execution must haltЯ не розумію. Як це глухий кут, якщо він зупиняється? Ви маєте на увазі, що це буде чекати чогось, а не просто крутитися, як мудак?
Cruncher

@Cruncher Погляньте на приклад, який не працює. Виконання коду не припиняється, оскільки цикл продовжує працювати. Так, я маю на увазі очікування, а не прядіння.
Джастін

Отже, якщо ви можете змусити потік інтерфейсу чекати себе в Dyalog APL, чи означає це, що є якась надія отримати відповідь на javascript? Хоча я підозрюю, що таке мислення просто відкриє двері до цієї відповіді: Javascript 6 "wait ()" ... ermmm. ні. Пов'язане: Чи можлива нитка до самого тупика?
Натан Купер

Відповіді:


11

Діалог APL (10)

⎕TSYNC⎕TID

⎕TSYNCзмушує нитку чекати, поки дана нитка закінчиться, ⎕TIDдає поточну нитку.

Dyalog APL може розпізнати тупикові місця, тому він негайно відповідає

DEADLOCK

Найцікавіше, що вам навіть не потрібно розроджувати зайві нитки, змушуючи нитку очікувати користувальницького інтерфейсу досить багато.

Якщо це обман і фактично потрібні нові теми, ви можете зробити це в 27 символів:

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

F & xпрацює Fу новому потоці за значенням xі повертає ідентифікатор потоку. Тому:

  • {⎕TSYNC⎕TID+1}&0 створює потік, який буде синхронізуватися з потоком, ідентифікатор якого на один вищий, ніж його власний,
  • ⎕TSYNC& створює новий потік, який буде синхронізуватися з попереднім потоком, і який отримує ідентифікатор на один вище, ніж щойно створений потік (припускаючи, що більше нічого не створює потоки).
  • викликає нескінченний цикл (тому ми продовжуємо створювати нитки, поки не з’явиться тупик).

Це стане тупиком, як тільки буде створено другий потік до запуску першого, що досить скоро:

9:DEADLOCK

Збережіть 2 байти за допомогою: ⎕TSYNC 0'. ⎕TID` є 0.
Adám

8

Іди, 42

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

Вибачте, репутація, за те, що не забезпечили, як це працює. Це створює анонімний канал вкладок і читає з нього. Це призупиняє основний потік, поки значення не надсилається вниз по каналу, що, очевидно, ніколи не відбувається, оскільки жодні інші потоки не активні, і, таким чином, тупикова ситуація.


2
Як це працює?
Джастін

4

Рубі, 39 символів

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

Ідея використовувати перехресне з'єднання безсоромно викрадена з відповіді Яванна Куна на Java .

Ми можемо стригти чотири символи (до 35 ), якщо налаштувати код на певне середовище. IRB консолі JRuby однопоточний:

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


Це моє попереднє рішення:

отримати нитку, наклеєну на мютекс, легко:

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

але це не належна тупикова ситуація, оскільки друга нитка технічно не чекає першої нитки. Відповідно до Вікіпедії, "тримайте і чекайте" - це необхідна умова тупику. Перший потік не чекає, а другий потік нічого не містить.

Рубі, 97 95 символів

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

це класичний тупик. Дві нитки змагаються за два ресурси, намагаючись знову досягти успіху. Зазвичай вони застрягають протягом секунди на моїй машині.

Але якщо ви маєте нескінченно багато потоків (жодна з яких не споживає процесор нескінченно, а частина з них тупикова), це нормально,

Рубі, 87 85 символів

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

Згідно з моїм тестом, він провалюється, коли кількість ниток досягне приблизно 4700. Сподіваємось, він не вийде з ладу, поки кожен потік не мав шанс запуститись (таким чином, або замикаючи або закінчуючи, і звільняючи місце для нового). Згідно з моїм тестом, кількість ниток не зменшується після того, як стався збій, це означає, що під час тесту стався тупик. Також IRB помер після тесту.


навіщо вам потрібні додаткові oта pзмінні? Ви не можете просто пройти mі nдля нової теми?
Йоганнес Кун

@JohannesKuhn mі nє глобальними. Обидві нитки бачать їх в одному порядку. oі pє локальними нитками (підключені до ітерації циклу). Використання t[...], ймовірно, дорого коштує, і я не бачу кращого способу передачі параметрів до потоку, ніж через закриття. Додавання додаткових параметрів для newподовження коду на дві символи.
Джон Дворак

@JohannesKuhn Я сподіваюся, ви не заперечуєте, що я запозичив частину вашої логіки
Джон Дворак

Я не проти Хороша робота.
Йоганнес Кун

Якщо припустити, що ми в головній нитці, ми можемо поголити це до 32 символівT=Thread;T.new{T.main.join}.join
гістократ


4

Bash + GNU coreutils, 11 байт

mkfifo x;<x

Створює заблокований FIFO xв поточному каталозі (тому вам не потрібно мати файл під цим іменем). FIFO можна видалити так само, як і звичайні файли, тому очистити їх не повинно бути складно.

У FIFO є сторона запису та сторона читання; намагання відкрити один блок, поки інший процес не відкриє інший, і, здається, це було навмисно розроблено як примітив синхронізації. Зважаючи на те, що тут є лише одна нитка, як тільки ми спробуємо її відкрити <x, ми застрягли. (Ви можете зняти тупик, надіславши відповідне повідомлення про FIFO в іншому процесі.)

Це різний вид тупику, ніж той, коли є два ресурси, і дві нитки мають один і потребує іншого; швидше, у цьому випадку ресурсів нульові, і процес потрібен. Виходячи з інших відповідей, я вважаю, що це враховується, але я можу зрозуміти, як пурист із тупику може захотіти заборонити відповідь.

Подумайте про це, я насправді можу придумати три ситуації, схожі на тупик:

  1. "Традиційний" тупик: по дві нитки очікують на звільнення блокування, яке тримається за іншим потоком.

  2. Один потік очікує звільнення блокування, але він тримає сам замок (і тим самим блокує себе від можливості звільнити його).

  3. Один потік очікує звільнення примітиву синхронізації, але примітив синхронізації запускається в природно заблокованому стані і повинен бути розблокований зовні, і нічого для цього не запрограмовано.

Це тупик типу 3, який кардинально відрізняється від двох інших: теоретично ви могли б написати програму, щоб розблокувати примитив синхронізації, про який йдеться, а потім запустити його. Однак, те саме стосується тупиків типу 1 і 2, враховуючи, що багато мов дозволяють звільнити замок, яким ви не володієте (ви цього не повинні і не мали б цього робити, якби у вас виникли причини користуйтеся замками в першу чергу, але це працює ...). Також варто розглянути таку програмуmkfifo x;<x;echo test>x; така програма, яка протилежна тупику 2-го типу (вона намагається відкрити обидва кінці FIFO, але не може відкрити один кінець, поки не відкриє інший кінець), але це було зроблено з цього за допомогою додавання додаткових код, який ніколи не працює після цього! Я гадаю, що проблема полягає в тому, що блокування замикається чи ні, залежить від наміру використання блокування, тому важко визначити об'єктивно (особливо у такому випадку, коли єдиною метою блокування є навмисне створення тупикового блоку ).



2

Баш з глібком, 6 байт

Вибачте за відродження старої нитки, але не втримався.

Як корінь:

pldd 1

Від людини pldd :

BUGS
Оскільки glibc 2.19, pldd порушено: він просто зависає при виконанні. Незрозуміло, чи це колись буде виправлено.


Немає проблем з відповідями на старий протектор, доки оригінал не був чутливим до часу.
Ad Hoc Garf Hunter

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

Ні. Thread.join()кидає an InteruptedException, що не є підкласом Error.
Йоханнес Кун

2

Ткл, 76

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

Тупик.

Це створює нову тему і іншому потоку повідомляє надіслати мою нитку повідомленням (сценарій для виконання).

Але відправлення повідомлення на іншу тему зазвичай блокується, поки сценарій не буде виконаний. І хоча воно блокується, жодне повідомлення не обробляється, тому обидва теми чекають, коли інший обробить своє повідомлення.


як це працює?
Джон Дворак

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

Скала, 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 lezy призупиняється, поки умова не буде виконана. Цю умову можна виконати, лише прочитавши значення ледачого val x - інший потік, який повинен виконати цю умову, не може цього зробити. Таким чином, утворюється кругова залежність, і ледачий вал не може бути ініціалізований.


Як це працює?
Аддісон Кримп

Я додав пояснення.
Мартін Зелер

1

Котлін, 35/37/55 байт

Загальна тема: Thread.currentThread().join().

Виключаючи помилки JVM / дуже спеціалізований код проти цього подання, цей shoud ніколи не повернеться, оскільки поточний потік виконання тепер відключений, чекаючи, коли він загине.


Властивість зла: 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а потім терпляче чекаємо, коли кожен з них вийде ... що станеться приблизно ніколи, оскільки процес буде чекати на себе.

Редагувати - Збережено 5 байт завдяки Роману Гряфу


(gps)|%{$_.waitforexit()}на три байти коротше і чекає, поки всі процеси завершаться.
Роман Ґраф

@ RomanGräf Дійсно, але паролі gpsв цьому випадку не потрібні , тому в цілому збережено 5 байт.
AdmBorkBork

0

C (тільки для Linux), 31 байт - спробуйте онлайн!

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

Системний виклик 240 (0xf0) - це futex (2) , або швидкий mutex файлу користувача. У документації зазначено, що перший аргумент - вказівник на футекс, другий аргумент - операція (0 означає FUTEX_WAIT, тобто чекайте, поки інший потік розблокує футекс). Третій аргумент - це значення, яке ви очікуєте, що він має ще зафіксований, а четвертий - вказівник на час очікування (NULL за відсутність таймауту).

Очевидно, оскільки немає іншої теми для розблокування футексу, він буде чекати вічно в самонанесеному тупику. Можна перевірити (за допомогою topабо іншого менеджера завдань), що процес не використовує час процесора, як це слід очікувати від тупикової потоку.


0

Джулія 0,6 , 13 байт

Мова новіша від питання. Зачекайте на завдання (як, наприклад, звичайна програма, в даний час буде в одній темі, в майбутніх версіях 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 автоматично не припинив її), але протягом більшої частини цього часу не було використання процесора, часу, витраченого програмою, і часу, витраченого ядром від імені програми.

Щоб отримати ще більш вагомі докази, ви можете запустити Ratiofall під налагоджувачем на рівні системного виклику, таким як strace; якщо це зробити в Linux, покаже перекладач, що блокує futexсистемний виклик, який намагається зняти блокування, яке ніколи не буде звільнено.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.