Державні машини проти ниток


24

Алан Кокс одного разу сказав: "Комп'ютер - це державна машина. Нитки призначені для людей, які не можуть програмувати державні машини".
Оскільки запитувати Алана прямо не є варіантом для мене смиренного, я б швидше запитав тут: як можна досягти багатопотокової функціональності в мові високого рівня, наприклад, Java, використовуючи лише одну нитку та стан машини? Наприклад, що робити, якщо потрібно виконати 2 дії (робити обчислення та робити введення / виведення), і одна діяльність може заблокувати?
Чи використовується "лише державна машина" способом життєздатна альтернатива багатопотоковій мові високого рівня?


4
Комп'ютер - це також машина Тьюрінга. Тим не менш, це не обов'язково корисно програмувати, як машина Тьюрінга. Стек в імперативних мовах - надзвичайно корисна річ, а багатопотокові програми дозволяють зберігати кілька стеків у пам’яті одночасно. Зробити те ж саме в державній машині, безумовно, можливо, але тим більше безладно.
титон

10
Алан був розробником ядра ОС; це був його доміан . Тож його цитата повинна сприйматися в цьому контексті. Він би програмував "проти металу", де має сенс використовувати таку модель. Після того, як ОС абстрагує обладнання та його внутрішні властивості (що "комп'ютер - це державна машина ..."), ви маєте можливість та користь використовувати інші моделі, що мають більше сенсу у вашому домені . Майже в кожній грі важко використовуються державні машини.
Стівен Еверс

2
Різьблення ниток - це просто функція ОС для автоматичного управління деякими комутаторами стану машини, якщо ви хочете. Очевидно, ви можете створити величезну державну машину, яка все керуватиме власно, але це складніше. Те саме можна сказати і про процеси. Можна сказати, що процеси призначені для людей, які також не можуть програмувати державні машини. Але абстракція надає вам набагато простіший і менш схильний до помилок інтерфейс. На мою думку, це просто ще одна «крута цитата», яку слід почути, обміркувати, а потім ігнорувати насправді.
Ям Маркович

2
"Але абстракція [нитка] надає вам набагато простіший і менш схильний до помилок інтерфейс." Це здається помилковим. Кількість людей, які помиляються з безпекою потоків, вказують на те, що це, мабуть, викликає помилки.
S.Lott

1
Багато коментарів та відповідей тут трактують цитату як антизадачу в цілому; Я вважаю, що Алан Кокс - це просто антитіла, і він виступає за використання декількох процесів для досягнення багатьох цілей, для яких люди використовують нитки. Майте на увазі, що він хакер Unix: fork FTW. Я не знайшов від нього жодних коментарів безпосередньо до цитати, але ось Ларрі Маквей із списку розсилки ядра Linux, який йде в цьому напрямку: lkml.indiana.edu/hypermail/linux/kernel/0106.2/0405.html
Martin B

Відповіді:


25

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

На одноядерному комп’ютері насправді нічого не відбувається "одночасно". Це все просто перемежоване виконання.

Існує багато, багато способів досягти переплетення. Багато.

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

  • T1-перед блокуванням
  • Т1-із замком
  • T1-після блокування
  • T2-перед блокуванням
  • Т2-із замком
  • T2-після блокування

[Це може бути в циклі або мати більше замків або будь-що інше. Все це робиться довше, не складніше.]

Етапи T1 повинні виконуватись у порядку (T1-до, T1-з, T1-після), а кроки T2 повинні виконуватись у порядку (T2-до, T2-з, T2-після).

Крім обмеження "в порядку", їх можна переплутати будь-яким способом. Будь-який спосіб. Їх можна запустити, як зазначено вище. Іншим дійсним замовленням є (T1-перед, T2-раніше, T2-замок, T1-замок, T2-після, T1-після). Діючих замовлень дуже багато.

Зачекайте.

Це просто державна машина з шістьма станами.

Це недетерміновані автомати з кінцевим станом. Упорядкованість станів T1-xxx зі станами T2-xxx невизначена і не має значення. Тож є місця, де «наступний стан» - це кидання монети.

Наприклад, коли FSM запускається, T1-перед або T2-before є обома законними першими станами. Киньте монету.

Скажімо, це було раніше T1. Зробити це. Коли це зроблено, є вибір між T1-з і T2-раніше. Киньте монету.

На кожному кроці FSM буде два варіанти (дві нитки - два варіанти) і кидання монети може визначити, який конкретний стан слід.


Дякую, приємне пояснення. А як щодо багатоядерної машини? Я думаю, немає явного способу експлуатації ядер у державній машині Java? Для цього потрібно покладатися на ОС, правда?
Віктор Сорокін

5
Багатоядерна машина робить планування трохи складнішим. Однак усі ядра записуються в єдину загальну пам'ять, тому впорядкування запису пам'яті між двома ядрами означає, що - по суті, ми повернулися до перемежованого виконання записів пам'яті. ОС використовує сердечники, і СП використовує це. Не потрібно думати про це двічі.
S.Lott

9

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

Нитки корисні, якщо ви не можете обійти блокування. Жодна фундаментальна діяльність на комп’ютері не є справді блокуючою, просто багато з них реалізовані таким чином для зручності використання. Замість повернення символу або "читання не вдалося" функція читання блокується, поки не буде прочитаний весь буфер. Замість перевірки на відповідне повідомлення у черзі та повернення, якщо жодного не знайдено, функція підключення чекає відповіді.

Ви не можете використовувати функції блокування в машині стану (принаймні тієї, якій не можна дозволити "заморожувати").

І так, використання державної машини є життєздатною альтернативою. У системах реального часу це єдиний варіант, система, що забезпечує рамки для машини. Використання ниток та функцій блокування - це просто "простий вихід", оскільки зазвичай один виклик функції блокування замінює приблизно 3-4 стану в машині стану.


Помилка кодової сторінки в програмі, що реалізується в одному контексті, принципово блокує. За визначенням, код, який має єдиний контекст виконання, не може досягти прогресу, поки не з'явиться наступний фрагмент коду в потоці.
Девід Шварц

1
@David Schwartz: правда, це принципово блокує; але це не «операція», оскільки це не те, що робить заблокований код, це щось, що з ним відбувається.
Хав'єр

1
Читання файлів принципово не блокує - його завжди можна розділити на запит зчитування заданого місця та отримання даних із буферів після того, як запит був виконаний. А помилка сторінки - це рішення для випадкових / евристичних змін. Це відбудеться, якщо даний стан було введено до того, як будуть доступні всі необхідні для його виконання дані - відсутність передбачуваності, що суперечить концепції державної машини. Якщо операції підкачки та підміни є частиною стану машини, помилка сторінки не відбудеться.
СФ.

1
@David Schwartz: Визначення "блокуючої" поведінки завжди підпадає під вимоги "реального часу". Наприклад: Помилка кодової сторінки вважається неблокувальною для програми, яка потребує чутливості в порядку сотень мілісекунд. З іншого боку, якщо програма має суворі вимоги в режимі реального часу, вона взагалі не використовує віртуальну пам'ять.
МаР

1
@Mar: ... або використовувати алгоритми детермінованих свопів, які гарантують отримання необхідних даних, перш ніж це стане необхідним.
СФ.

9

Як можна досягти багатопотокової функціональності на мові високого рівня, наприклад, на Java, використовуючи лише одну нитку та стан машини? Наприклад, що робити для виконання 2-х дій (робити обчислення та робити введення-виведення), і одна діяльність може блокуватись?

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

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

Чи використовується "лише державна машина" способом життєздатна альтернатива багатопотоковій мові високого рівня?

Життєздатний? Звичайно. Здоровий? Іноді. Незалежно від того, чи використовуєте ви нитки або якусь форму домашньої кооперативної багатозадачності (наприклад, державні машини), залежить від компромісів, які ви готові зробити.

Нитки спрощують розробку завдань до того моменту, коли ви можете ставитися до кожного як до власної програми, яка має спільний доступ до даних з іншими. Це дає вам свободу зосередитись на роботі, а не всьому управлінському та господарському господарству, необхідному для того, щоб він працював ітерацією за раз. Але оскільки жодна добра справа не залишається безкарною, ви сплачуєте за все це зручність в контекстних комутаторах. Маючи багато потоків, які дають процесор після мінімальної роботи (добровільно або виконуючи щось, що блокує, як-от введення-виведення), може з'їсти багато процесорного часу, роблячи переключення контексту. Це особливо актуально, якщо операції з блокування рідко блокуються дуже довго.

Є деякі ситуації, коли шлях спільної роботи має більше сенсу. Мені колись довелося написати програмне забезпечення для користувальницької частини обладнання, яке передало багато каналів даних через інтерфейс, нанесений на пам'ять, який вимагав опитування. Кожен канал був об'єктом, побудованим таким чином, що я міг або дозволити йому працювати як потік, або повторно виконувати один цикл опитування.

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

Однопоточна версія, яка робила навіть ітерації кожного каналу, проходила, як ошпарена мавпа, і навантаження на систему падало, як скеля. Штраф, який я заплатив за додатковий виступ, мав сам перемикати завдання. У цьому випадку код зробити це було досить просто, щоб витрати на його розробку та підтримку були варті того, щоб покращити продуктивність. Я думаю, це справді суть. Якби мої теми були, які сиділи навколо, чекаючи, коли якийсь системний дзвінок повернеться, вправа, мабуть, не вартувала б цього.

Це підштовхує мене до коментаря Кокса: теми не призначені виключно для людей, які не вміють писати державні машини. Деякі люди цілком здатні це робити, але вирішують скористатися консервованою машиною (тобто ниткою) в інтересах зробити роботу раніше чи з меншою складністю.


2

що робити, якщо потрібно виконати 2 дії (робити обчислення та робити введення / виведення), і одна діяльність може блокувати?

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

На моєму прочитанні оригінальної електронної пошти Кокса (нижче) він зазначає, що те, що введення різьби, не відповідає масштабам. Я маю на увазі, що робити, якщо є 100 запитів вводу / виводу? 1000? 10000? Кокс вказує, що наявність великої кількості ниток може призвести до серйозних проблем:

Від: Алан Кокс (alan@lxorguk.ukuu.org.uk)
Дата: Пт 21 січня 2000 - 13:33:52 EST

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

Це найменше ваших турбот. 1000 потоків - це 8 Мб стеків ядра, і достатньо комутації завдань, щоб бути впевненим, що ви можете вимкнути більшість кешу. Комп’ютер - це державна машина. Нитки призначені для людей, які не вміють програмувати державні машини.

Є багато випадків, коли Linux, безумовно, не допомагає ситуації, зокрема, асинхронному блоку вводу-виводу.

Алан

Джерело: Re: Цікавий аналіз потоку ядер Linux для IBM (архіви списку розсилки linux-ядра)


1
  • Теоретично це правда. У реальному житті потоки - це лише ефективна абстракція, яка використовується для програмування такої державної машини. Вони настільки ефективні, що їх можна використовувати і для програмування Statecharts і мереж Петрі (тобто, паралельної поведінки, де державні машини в основному є послідовними).

  • Проблема з державними машинами - комбінаторний вибух. Кількість станів комп'ютера з оперативною пам’яттю 4G становить 2 ^ (2 ^ 32) стану (не рахуючи 2T дискового накопичувача).

  • Чоловікові, єдиним інструментом якого є молоток, кожна проблема виглядає як цвях.


1

Нитки - єдиний варіант у двох випадках:

  • використовувати кілька ядер без поділу пам'яті.
  • впоратися із зовнішнім кодом, який блокує.

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

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


1
Найкраще з обох світів: Малі, блокуючі стаціонарні машини в нитках створюють дуже простий код програми. І нитки чудово розбиваються до ядер, якщо ви їх отримали. Якщо все не має принаймні двох ядер, це скоро. тобто чотирьохядерні телефони, що базуються на
операціях

0

Хороший спосіб взаємодіяти між державними машинами та багатопотоковою роботою - подивитися на обробників подій GUI. У багатьох програмах / рамках GUI використовується один потік GUI, який запитує можливі джерела введення та викликає функцію для кожного отриманого вводу; по суті, це можна записати як величезний перемикач:

while (true) {
    switch (event) {
        case ButtonPressed:
        ...
        case MachineIsBurning:
        ....
    }
}

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

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

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

Отже, щоб відповісти на остаточне запитання: Так, державна машина - це альтернатива, більшість графічних інтерфейсів працюють саме так з потоком GUI. Просто не натискайте на державну машину занадто далеко, це стає дуже незрозумілим дуже швидко.


0

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

Абстрагування використання різьблення робить більш складними паралельні системи простішими у впровадженні, але в кінцевому підсумку всі паралельні системи мають однакові проблеми. Класичні проблеми, такі як Deadlock / Livelock та інверсія пріоритетів , так само можливі для державних машинних систем, оскільки вони мають спільну паралельну пам'ять , систему NUMA або навіть CSP , якщо вона є досить складною.


0

Я не думаю, що це - звичайно, державні машини - це дуже «елегантна» обчислювальна концепція, але, як ви кажете, вони досить складні. І складні речі складно виправити. А речі, які не є правильними, просто порушуються, тому, якщо ви не геній передбачуваного статусу Алана Кокса, дотримуйтесь речей, які ви знаєте, що працюють, залишайте «розумне кодування» навчальним проектам.

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

У деяких випадках державна машина стане кращим вибором - я думаю про вбудовані типи матеріалів зараз, де використовуються деякі моделі державних машин, і використовуються повторно та більш формалізовано (тобто належна інженерія :))

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

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


2
Державні машини зовсім не складні! Комплексні державні машини складні, але так само всі складні системи: o)
MaR

2
-1 для "не намагайся". це найгірша порада, яку ви можете дати.
Хав'єр

1
-1 "Не намагайся"? Це просто нерозумно. Я також заперечую ваше твердження, що державні машини важкі. Як тільки ви потрапите в щось на кшталт герархальної нечіткої державної машини ... то так, це трохи складніше. Але проста державна машина? Це досить основні речі, які кожен 2-й рік дізнавався, коли я навчався в школі.
Стівен Еверс

дозвольте мені перефразувати біт "Dont try" ...
gbjbaanb

0

Хороший приклад правильного використання машини машини замість ниток: nginx vs apache2. Як правило, ви можете припустити, що nginx обробляє всі з'єднання в одному потоці, apache2 робить потік на з'єднання.

Але для мене використання державних машин проти потоків дуже схоже на використання ідеально ручної роботи asm vs java: ви можете домогтися незрозумілих результатів, але для цього потрібні багато зусиль програміста, багато дисципліни, зробити проект складнішим і вартим лише тоді, коли його використовують багато інших програмістів. Тож якщо ви є тим, хто хоче створити швидкий веб-сервер - використовуйте стан машини та асинхронізуйте IO. Якщо ви пишете проект (не бібліотека, яку слід використовувати всюди) - використовуйте теми.

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