Коли вам знадобляться "сотні тисяч" ниток?


31

Erlang, Go і Rust всі так чи інакше стверджують, що вони підтримують паралельне програмування за допомогою дешевих "потоків" / процедур. У FAQ поширені запитання :

Практично створити сотні тисяч goututines в одному адресному просторі.

Rust Підручник каже:

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

Документація Ерланга говорить:

Початковий розмір купи за замовчуванням у 233 слова є досить консервативним для того, щоб підтримувати системи Erlang із сотнями тисяч і навіть мільйонами процесів.

Моє запитання: який тип програми вимагає стільки одночасних потоків виконання? Тільки найзайнятіші веб-сервери приймають навіть тисячі одночасно відвідувачів. Програми типу «бос-працівник / диспетчер», які я писав, я писав про зменшення повернень, коли кількість потоків / процесів значно більше, ніж кількість фізичних ядер. Я думаю, це може мати сенс для чисельних програм, але насправді більшість людей делегує паралелізм третім сторонам бібліотекам, написаним у Fortran / C / C ++, а не цим мовам нового покоління.


5
Я думаю, що джерело вашої плутанини полягає в наступному: ці мікропотоки / завдання / тощо в першу чергу не призначені в якості заміни потокам / процесам ОС, про які ви говорите, а також не використовуються для поділу легко паралелізуючого великого куска скорочення чисел між декількома ядрами (як ви правильно зауважили, немає сенсу мати 100k ниток на 4 ядрах для цієї мети).
us2012

1
Тоді для чого вони призначені? Можливо, я наївний, але я ніколи не стикався з ситуацією, коли введення процедур / тощо полегшило б програму з однорядним виконанням. І мені вдалося досягти "низьких" рівнів одночасності з процесами, які в Linux я можу запускати сотні чи тисячі, не порушуючи поту.
user39019

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

5
Ідея асинхронії на основі завдань на основі асинхронії на основі потоку полягає в тому, що код користувача повинен концентруватися на тих завданнях, які повинні відбуватися, а не на управлінні працівниками, які виконують ці завдання. Подумайте про нитку як працівника, якого ви наймаєте; наймати працівника дорого, і якщо ви це робите, ви хочете, щоб вони наполегливо працювали над якомога більшою кількістю завдань у 100% часу. Безліч систем можна охарактеризувати як такі, що мають сотні чи тисячі невиконаних завдань, але вам не потрібні сотні чи тисячі працівників.
Ерік Ліпперт

Продовжуючи коментар @ EricLippert, є кілька ситуацій, коли існують сотні тисяч завдань. Приклад №1: декомпозиція паралельних даних, таких як обробка зображень. Приклад №2: сервер, що підтримує сотні тисяч клієнтів, кожен з яких потенційно може видавати команду в будь-який час. Кожне завдання вимагало б свого "легкого контексту виконання" - здатності запам'ятовувати, у якому стані він знаходиться (протоколи зв'язку), та команди, яку він виконує в даний час, і мало іншого. Легка вага можлива до тих пір, поки кожен має неглибокий стек викликів.
rwong

Відповіді:


19

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

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


15

Це може допомогти подумати про те, що Ерланг спочатку був розроблений, а саме - управління телекомунікаціями. Такі дії, як маршрутизація, комутація, збір / агрегація датчиків тощо.

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

Ця стаття може бути корисною.


11

Мовою, де вам заборонено змінювати змінні, простий акт підтримання стану вимагає окремого контексту виконання (який більшість людей називає потік, а Ерланг називає процес). В основному все - це робітник.

Розглянемо цю функцію Erlang, яка підтримує лічильник:

counter(Value) ->
    receive                               % Sit idle until a message is received
        increment -> counter(Value + 1);  % Restart with incremented value
        decrement -> counter(Value - 1);  % Restart with decremented value
        speak     ->
            io:fwrite("~B~n", [Value]),
            counter(Value);               % Restart with unaltered value
        _         -> counter(Value)       % Anything else?  Do nothing.
    end.

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

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


1
У вашому останньому застосуванні counter/1слід використовувати малі літери c;) Я спробував це виправити, але StackExchange не любить 1-символьних змін.
d11wtq

4

Моє запитання: який тип програми вимагає стільки одночасних потоків виконання?

1) Той факт, що мова "важить" означає, що є менше шансів, що вам доведеться скинути цю мову, коли все стане складніше в дорозі. (Це називається концепцією "Весь продукт".) Багато людей кидають Apache на Nginx саме з цієї причини. Якщо ви десь близькі до "жорсткої межі", накладеної накладними потоками, ви злякаєтесь і почнете думати про способи подолати це. Веб-сайти ніколи не можуть передбачити, скільки трафіку вони отримають, тому витратити трохи часу на те, щоб змінити масштабованість.

2) Одна програма на запит лише на початку. Існує маса причин використовувати горотини всередину.

  • Розглянемо веб-додаток з одночасними запитами 100, але кожен запит генерує 100 запитів із зворотнього зв'язку. Очевидний приклад - агрегатор пошукових систем. Але майже будь-яка програма може створювати програми для кожної "області" на екрані, а потім генерувати їх самостійно, а не послідовно. Наприклад, кожна сторінка на Amazon.com складається з 150+ додаткових запитів, зібраних саме для вас. Ви не помічаєте, оскільки вони паралельно, а не послідовні, і кожна "область" - це власна веб-служба.
  • Розгляньте будь-яку програму, де надійність та затримка є першорядними. Ви, мабуть, хочете, щоб кожен вхідний запит знімав декілька запитів, що повертаються , і повертати будь-які дані, що повертаються першими .
  • Розгляньте будь-яке "приєднання клієнта", яке виконується у вашому додатку. Замість того, щоб сказати "для кожного елемента, отримати дані", ви можете розкрутити купу горотин. Якщо у вас є купа рабних БД для запиту, ви чарівно пройдете N час швидше. Якщо цього не зробити, це не буде повільніше.

зменшення повернень повертається, коли кількість потоків / процесів значно більше, ніж кількість фізичних ядер

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

Як і у слайдах, зв'язаних вище, сумісність у вашому коді є способом організації проблеми. Не мати гороутин - це не мати структури даних Map / Dictonary / Hash на вашій мові. Ви можете обійтись без цього. Але як тільки у вас є, ви починаєте використовувати його скрізь, і це дійсно спрощує вашу програму.

У минулому це означало "прокатати своє" багатопотокове програмування. Але це було складно і небезпечно - все ще не так багато інструментів, щоб переконатися, що ви не створюєте перегони. І як завадити майбутньому технічному обслуговувальнику помилитися? Якщо ви подивитесь на великі / складні програми, ви побачите, що вони витрачають багато ресурсів у цьому напрямку.

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


2

Для Erlang прийнято мати один процес на з'єднання або інше завдання. Так, наприклад, потоковий аудіосервер може мати 1 процес на підключеного користувача.

Erlang VM оптимізований для обробки тисяч або навіть сотень тисяч процесів, роблячи контекстні комутатори дуже дешевими.


1

Зручність. Ще коли я почав займатися багатопотоковим програмуванням, я робив багато моделювання та розробки ігор на стороні для задоволення. Мені здалося, що це дуже зручно - просто відкрутити нитку для кожного окремого об'єкта і дозволити йому це робити самостійно, а не обробляти кожен через цикл. Якщо ваш код не порушується недетермінованою поведінкою і у вас немає зіткнень, це може полегшити кодування. Маючи потужність, доступну нам зараз, якби я повернувся до цього, я можу легко уявити собі спінінг на пару тисяч ниток через те, що маю достатню потужність для обробки та пам'ять для обробки багатьох дискретних об'єктів!


1

Простий приклад для Erlang, який був розроблений для спілкування: передача мережевих пакетів. Коли ви робите один http-запит, у вас можуть бути тисячі пакетів TCP / IP. Додайте до цього, що всі підключаються одночасно, і у вас є випадок використання.

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


-2

Деякі завдання з рендерінгу тут спадають на думку. Якщо ви робите довгий ланцюжок операцій на кожному пікселі зображення, і якщо ці опції можна паралелізувати, то навіть відносно невелике зображення 1024x768 потрапляє прямо в кронштейн "сотні тисяч".


2
Деякі роки тому я провів кілька років, займаючись обробкою зображень FLIR у режимі реального часу, стискаючи зображення 256х256 із швидкістю 30 кадрів в секунду. Якщо у вас є багато процесорів HARDWARE і безпроблемний спосіб розподілу ваших даних між ними, ОСТАННЕ, що ви хочете зробити, - це додати контекстну комутацію, суперечки пам’яті та кеш-пам'ять до фактичних обчислювальних витрат.
Джон Р. Стром

Це залежить від виконаної роботи. Якщо все, що ви робите, - це передача роботи в апаратне ядро ​​/ блок виконання, після чого ви можете фактично забути про це (і зауважте, що це так, як працюють графічні процесори, так що це не гіпотетичний сценарій), тоді підхід полягає в дійсний.
Максим Мінімус
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.