Що являє собою належне використання потоків у програмуванні?


13

Я втомився чути, що люди рекомендують використовувати лише один потік на процесор, тоді як багато програм використовують до 100 за процес! візьмемо для прикладу кілька загальних програм

vb.net ide uses about 25 thread when not debugging
System uses about 100
chrome uses about 19
Avira uses more than about 50

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


7
Ця рекомендація є широкою. Обмеження одного потоку на один процесор підходить лише для обчислювальних програм. Більшість програм пов'язані з IO, будь то мережевий трафік, доступ до диска або навіть оперативна пам'ять. Ось чому веб-сервери, бази даних тощо мають пули потоків із значно більшою кількістю потоків, ніж ядра процесора.
Кіліан Фот

2
"Мені майже кожного разу нагадують, що я не повинен використовувати більше одного потоку на процесор"? Чи можете ви розміщувати посилання чи приклади? Майже кожного разу?
S.Lott

2
"... люди рекомендують використовувати лише один потік у процесі." Хто ці люди? Планування склало істотний прогрес із темних віків.
Рейн Генріхс

2
У вас не повинно бути більше одного потоку інтерфейсу користувача на один процес.
СЛАкс

3
@Billy ONeal, ваша редакція зробила питання безглуздим
SK-логіка

Відповіді:


22

ви повинні використовувати лише один потік на один процесор,

Можливо, в HPC, де ви хочете максимальної працездатності - але в іншому випадку найглупіше, що я чув сьогодні!

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

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

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


9
Тепер ви здивуєте, що найглупіше, що ви коли-небудь чули!
Майкл К

3
@Michael - Я викладав магістрів та працював над оборонними контрактами - ти б не повірив найглупішим речам, що я чув!
Мартін Бекетт

1
Ми бачили їх на TheDailyWTF.com?
FrustratedWithFormsDesigner

Я зараз не можу їх знайти, але подивіться за цим посиланням social.msdn.microsoft.com/Forums/en-US/vbgeneral/thread/…
Smith

2
Майте щонайменше один потік, пов'язаний з процесором, на один процесор, виділений програмі. Пов'язані з IO потоки не є великою проблемою (крім пам’яті, яку вони споживають), і важливо пам’ятати, що додатки можуть бути обмежені лише для використання підмножини процесорів системи; зрештою, це (як правило) комп'ютер користувача / адміністратора, а не комп'ютер програміста.
Дональні стипендіати

2

Порада з однією ниткою на основу застосовується, коли метою є швидкість шляхом паралельного виконання.

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


1
Обробка, пов'язана з IO, може здійснюватися як один потік на джерело події, або кілька джерел подій можуть бути мультиплексовані на один потік. Мультиплексований код зазвичай є і складнішим, і більш ефективним.
Стипендіати Доналу

2

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

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

Для обчислень, пов'язаних з входом / виводом, ви, як правило, хочете по одному потоку для кожного незалежного "каналу" вводу / виводу, оскільки вони спілкуються з різною швидкістю, а потоки, заблоковані на каналі, не перешкоджають іншим потокам прогресувати.


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

Одна думка полягає в тому, що цей стиль створює незвичайні програми. Інша думка - це природний стиль, який мали мати програми. Данно про "неінтуїтивні" умови помилок; якщо у вас трапляється багато справ, і одна з них отримує помилку, переконайтеся, що вона правильно розповсюджується по асинхронних обчисленнях - проблема для багатьох мов [тупо, винятки Java не визначаються на межі потоку], але це не так проблема зі стилем програми. (Наш ланцюг програмування PARLANSE [див. Мою біографію] обробляє винятки через кордони потоку чисто, тому це можливо зробити правильно.)
Іра Бакстер

1

Правило для ниток полягає в тому, що ви хочете, щоб принаймні один "активний" (здатний негайно виконувати його команди за вказаний час процесора) робочий потік для кожного "блоку виконання", доступного на комп'ютері. "Одиниця виконання" - це один логічний процесор інструкцій, тому чотириядерний чотириядерний сервер Xeon з гіпертоком має 32 ЄС (4 мікросхеми, 4 ядра на мікросхемі, кожна з гіперпотоків). У вашому середньому Core i7 було б 8.

Один потік в ЄС - це найповніше використання потужності процесора за умови, що потоки завжди будуть у робочому стані; це майже ніколи не відбувається, оскільки потокам потрібен доступ до некешованої пам’яті, жорсткого диска, мережевих портів і т. д., яких вони повинні чекати, і для цього не потрібна активна увага процесора. Таким чином, ви можете додатково підвищити загальну ефективність за допомогою більшої кількості потоків, що стоять у черзі, та йдуть вперед. Це дійсно доходить; коли CPU перемикає потік, він повинен кешувати регістри потоку, покажчик виконання та іншу інформацію про стан, яка зазвичай зберігається в найсумісніших роботах ЄС і дуже швидко отримує доступ, дозволяючи іншим членам ЄС у цьому чіпі CPU забрати його. Також потрібні потоки в ОС, щоб вирішити, на який потік слід перейти. Нарешті, коли ЄС перемикає нитки, він втрачає підвищення продуктивності конвеєрних труб, якими користується більшість процесорних архітектур; перед перемиканням ниток він повинен промивати трубопровід. Але оскільки все це в середньому займає набагато менше часу, ніж просто чекати, коли жорсткий диск або навіть оперативна пам’ять повернуться з інформацією, це вартує витрат.

Однак, як правило, як тільки ви перевищуєте вдвічі більше «активних» потоків, ніж ЄС, ОС починає витрачати більше часу потоків планування ЄС, і ЄС витрачає більше часу на перемикання між ними, ніж фактично витрачається на активні потоки програм. Це точка неекономії масштабу; фактично знадобиться більше часу для запуску багатопотокового алгоритму, якщо ви до цього часу додали додатковий потік.

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


Якщо N - кількість потоків, а U - кількість одиниць, ОП поставило під сумнів правило "N = U". Ви розслабляєте це за правилом "U <= N <= 2 U". Я хотів би трохи пізніше сказати, що "N <= c U" для "досить малої" константи (відома програмісту) c є прийнятною (якщо показники показують розумні показники). Я б дуже занепокоєний, якщо кількість потоків може зрости до потенційно необмеженої кількості.
5gon12eder

1

Ви повинні використовувати одну нитку для:

Кожен процесор, який потрібно тримати зайнятий.

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

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

Кілька додаткових для захисту від несподіваного блокування, наприклад, помилок сторінки.

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

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


0

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

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


0

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

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

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