Як потоки заощаджують час?

Я вивчаю threading в C #. Однак я не можу зрозуміти, які аспекти потоків насправді покращують продуктивність.

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

22
Ваша програма може чекати щось інше, ніж ЦП - наприклад, дані, що передаються через мережу, або завантаження файлу з жорсткого диска. У цьому випадку може бути сенс, щоб це сталося в іншому потоці, а не блокувати основний потік у вашій програмі.
додано Автор John Sibly, джерело
Ваша програма може чекати щось інше, ніж ЦП - наприклад, дані, що передаються через мережу, або завантаження файлу з жорсткого диска. У цьому випадку може бути сенс, щоб це сталося в іншому потоці, а не блокувати основний потік у вашій програмі.
додано Автор John Sibly, джерело
різьблення є найбільш ефективним у багатоядерних середовищах
додано Автор Jonesopolis, джерело
"Оскільки потоки - це просто спільне використання часу, то як їх час виконання (час обертання) менший, ніж один поточний процес"? Час обертання - довший . Маючи більше потоків, ви можете працювати, поки інший потік не сплять, очікуючи сповіщення про пробудження (наприклад, у випадку, коли ви виконуєте синхронізацію), але, маючи більше потоків, ви створюєте додаткові роботи для своєї ОС/runtime.
додано Автор spender, джерело
"Оскільки потоки - це просто спільне використання часу, то як їх час виконання (час обертання) менший, ніж один поточний процес"? Час обертання - довший . Маючи більше потоків, ви можете працювати, поки інший потік не сплять, очікуючи сповіщення про пробудження (наприклад, у випадку, коли ви виконуєте синхронізацію), але, маючи більше потоків, ви створюєте додаткові роботи для своєї ОС/runtime.
додано Автор spender, джерело
Розглянемо інший сценарій, як і раніше на одноядерному процесорі: ви завантажуєте великий файл із веб-сайту. Цілком імовірно, що операція займе якийсь час і не буде повністю використовувати процесор. Нитка дозволяє використовувати запасний процесорний час для інших завдань.
додано Автор Kevin Gosse, джерело
Розглянемо інший сценарій, як і раніше на одноядерному процесорі: ви завантажуєте великий файл із веб-сайту. Цілком імовірно, що операція займе якийсь час і не буде повністю використовувати процесор. Нитка дозволяє використовувати запасний процесорний час для інших завдань.
додано Автор Kevin Gosse, джерело
Це питання також може вас зацікавити: unix.stackexchange.com/questions/80424/…
додано Автор Eric Lippert, джерело
Подумайте про це таким чином, якщо вам довелося випробувати "X", ви могли б зробити це швидше самостійно? Або це буде набагато швидше, якщо б ти та ще кілька людей все це робили?
додано Автор Brian, джерело
Подумайте про це таким чином, якщо вам довелося випробувати "X", ви могли б зробити це швидше самостійно? Або це буде набагато швидше, якщо б ти та ще кілька людей все це робили?
додано Автор Brian, джерело
Один із багатьох сценаріїв - вміст, який не має великого значення для вашого клієнта, може бути пов'язаний з такими параметрами: Надіслати листа клієнту для підтвердження при натисканні на відправку . Просто надішліть потік до вашої поштової функції і дайте відповідь перейти до клієнта. Чому клієнт повинен зачекати на SMTP-сервер для обробки/відправки (іншого вводу-виводу) електронної пошти, дозвольте йому грати з веб-сторінкою. Застосовується також для одноколісного
додано Автор Sumit Kapadia, джерело

11 Відповіді

У одноядерному ЦП перевага, яку ви отримуєте, - це асинхронність. Використання потоків - це один із способів досягнення цього (хоча це не єдиний спосіб).

Уявіть собі процес приготування їжі. Що, на вашу думку, швидше:

  1. Починайте кип'ятіння води. Зачекайте, поки він закінчиться.
  2. Додати деякі локшини. Зачекайте, доки їх не буде приготовано.
  3. Вимийте/готуйте кілька овочів.
  4. Обсмажити овочі.
  5. Покладіть на тарілку та подайте.

Або натомість:

  1. Почніть кип'ятіння води.
  2. Хоча вода кипить, обмиває/готує овочі.
  3. Додати лапшу до горщика з окропом.
  4. Обсмажте овочі під час готування локшини.
  5. Покладіть на тарілку та подайте.

З мого досвіду, другий - швидше.

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

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

28
додано
Я хотів би додати, що потоки є поширеним способом досягнення асинхронності (не зважаючи на їх відносно високу вартість), оскільки вони, як правило, набагато простіші, ніж альтернатива (зворотні виклики). Є кілька мов, які намагаються зробити цю різницю меншою, наприклад C# 5.0 або Go.
додано Автор svick, джерело
@ Сервіс - приємний приклад, це б краще пояснити сценарій, якщо б ви мали одну плиту з плитою та виконували паралельно роботу, що не пов'язана з плитою. хоча ви зрозуміли це, дуже дякую.
додано Автор Sumit Kapadia, джерело

У одноядерному ЦП перевага, яку ви отримуєте, - це асинхронність. Використання потоків - це один із способів досягнення цього (хоча це не єдиний спосіб).

Уявіть собі процес приготування їжі. Що, на вашу думку, швидше:

  1. Починайте кип'ятіння води. Зачекайте, поки він закінчиться.
  2. Додати деякі локшини. Зачекайте, доки їх не буде приготовано.
  3. Вимийте/готуйте кілька овочів.
  4. Обсмажити овочі.
  5. Покладіть на тарілку та подайте.

Або натомість:

  1. Почніть кип'ятіння води.
  2. Хоча вода кипить, обмиває/готує овочі.
  3. Додати лапшу до горщика з окропом.
  4. Обсмажте овочі під час готування локшини.
  5. Покладіть на тарілку та подайте.

З мого досвіду, другий - швидше.

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

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

28
додано
Я хотів би додати, що потоки є поширеним способом досягнення асинхронності (не зважаючи на їх відносно високу вартість), оскільки вони, як правило, набагато простіші, ніж альтернатива (зворотні виклики). Є кілька мов, які намагаються зробити цю різницю меншою, наприклад C# 5.0 або Go.
додано Автор svick, джерело
@ Сервіс - приємний приклад, це б краще пояснити сценарій, якщо б ви мали одну плиту з плитою та виконували паралельно роботу, що не пов'язана з плитою. хоча ви зрозуміли це, дуже дякую.
додано Автор Sumit Kapadia, джерело

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

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

По-перше, як Серві та інші зазначають у своїх відповідях, якщо робочі місця не пов'язані з процесором , то, очевидно, тут може бути деяке прискорення, тому що , поки процесор очікує на очікування диску або мережа, щоб повернутися, це може зробити роботу іншої потоку .

Але, припустимо, у вас є два завдання, пов'язані з процесором, один процесор і будь-які два потоки або один потік. У сценарії "один потік" це виглядає так:

  • 100% роботи роботи 1. Припустимо, що це займає 1000 мс.
  • 100% роботи роботи 2. Припустимо, що це займе 1000 мс.

Загальний час: дві секунди. Загальна кількість виконаних робіт: два. Але ось важливий біт: клієнт, який чекав роботи 1, пройшов свою роботу лише через одну секунду. Клієнт, який чекав роботи 2, повинен був зачекати дві секунди.

Тепер, якщо у нас є дві нитки і один процесор, це виглядає так:

  • Зробіть 10% роботи завдання 1, за 100 мс.
  • Зробіть 10% роботи завдання 2, за 100 мс.
  • Зробіть 10% роботи роботи 1
  • Зробіть 10% роботи роботи 2 ...

Again, total time two seconds, but this time the client that was waiting for job 1 got their job done in 1.9 seconds, nearly 100% slower than the one-thread scenario!

Отже, це моральність історії тут, і ви цілком правильно вкажете. Якщо виконуються наступні умови:

  • Завдання пов'язані з процесором
  • Є більше потоків, ніж процесори
  • Робота корисна лише для її кінцевого результату

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

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

Тепер, якщо будь-який з цих умов буде виконаний не , додаванням інших потоків є гарна ідея.

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

  • Якщо є холодні процесора, то додавання інших потоків дозволяє запланувати ці процесори.

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

25
додано

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

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

По-перше, як Серві та інші зазначають у своїх відповідях, якщо робочі місця не пов'язані з процесором , то, очевидно, тут може бути деяке прискорення, тому що , поки процесор очікує на очікування диску або мережа, щоб повернутися, це може зробити роботу іншої потоку .

Але, припустимо, у вас є два завдання, пов'язані з процесором, один процесор і будь-які два потоки або один потік. У сценарії "один потік" це виглядає так:

  • 100% роботи роботи 1. Припустимо, що це займає 1000 мс.
  • 100% роботи роботи 2. Припустимо, що це займе 1000 мс.

Загальний час: дві секунди. Загальна кількість виконаних робіт: два. Але ось важливий біт: клієнт, який чекав роботи 1, пройшов свою роботу лише через одну секунду. Клієнт, який чекав роботи 2, повинен був зачекати дві секунди.

Тепер, якщо у нас є дві нитки і один процесор, це виглядає так:

  • Зробіть 10% роботи завдання 1, за 100 мс.
  • Зробіть 10% роботи завдання 2, за 100 мс.
  • Зробіть 10% роботи роботи 1
  • Зробіть 10% роботи роботи 2 ...

Again, total time two seconds, but this time the client that was waiting for job 1 got their job done in 1.9 seconds, nearly 100% slower than the one-thread scenario!

Отже, це моральність історії тут, і ви цілком правильно вкажете. Якщо виконуються наступні умови:

  • Завдання пов'язані з процесором
  • Є більше потоків, ніж процесори
  • Робота корисна лише для її кінцевого результату

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

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

Тепер, якщо будь-який з цих умов буде виконаний не , додаванням інших потоків є гарна ідея.

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

  • Якщо є холодні процесора, то додавання інших потоків дозволяє запланувати ці процесори.

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

25
додано

Більшість коментарів у вас є правильними, але я також викинув два мої центри (і перерахуйте коментарі тут):

Jonesy: "threading is most efficient in multi core environments" -> Yes, but this is a single core cpu...so I'll come back to this.

KooKiz і Джон Смібл: вони обидва згадують I/O. Ваша машина не хруснула разом на повній потужності 100% часу. Існує чимало інших речей, які забирають час, і під час цих подій ваш процесор отримує перерву.

(точка відліку: введення/виводу може бути мережевою передачею, читати жорсткий диск/RAM, запит SQL тощо. Все, що приносить нові дані до ЦП або завантажує дані з ЦП)

Ці переривання - це час, коли ваш процесор може робити інші речі. Якщо у вас є єдиний процесор (ми будемо ігнорувати hyperthreading на даний момент), і однопоточне додаток, то він працює щасливим, як це можливо. Однак це не відбувається постійно. Планування процесора дасть йому цикл або два, а потім переходите до чогось іншого, потім через деякий час поверніться до вашої програми, додайте ще кілька циклів, перемістіться і т. Д. Це дає ІЛЮСІОН можливість робити "кілька речі одночасно "на одному процесорі з одним ядром.

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

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

Зауважте, що це малоймовірно, що ви побачите прискорення 2: 1. Набагато імовірніше, що ваша 2-річна програма буде бачити лише 10-20% швидкості, якщо це буде. Пам'ятайте, що "інший" потік (який у будь-який момент - це те, що НЕ виконує введення/виведення) буде дійсно працювати лише на повну потужність, коли перша потока виконує введення/виведення.

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

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

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

3
додано
Я думав, що DRAM працює на замовлення десятків наносекунд . Десятки мілісекунд - затримка жорстких дисків.
додано Автор svick, джерело
Чи можлива пам'ять читання вважати введенням/виводом тут? Наскільки я знаю, це досить швидко, так що перемикач контекстів під час очікування читання не має сенсу (ймовірно, за винятком HyperThreading, але ви сказали, що ігнорувати це).
додано Автор svick, джерело
@svick: типова споживча пам'ять працює на якій, 7-10 мс? в такому періоді може трапитися багато циклів. Я чесно не знаю, чи відбудеться контекстний перемикач, але я б це підозрював. Відповідно до askldjd.wordpress.com/2011/02/20/length-of-the-thread-quantum , квант для цього конкретного процесора становить близько 5 мс ... що складає 13 мільйонів процесорних циклів. З огляду на ці цифри, я б припустив, що оперативна пам'ять вважається IO, оскільки ви можете відмовитися від 2 або більше повних значень просто, щоб отримати змінну.
додано Автор Russell Uhl, джерело
@svick: Вибачте, ви правильні. Тим не менш, ти все ще втрачає 13 тисяч циклів. У цьому випадку я не знаю, чи це вважається прийнятною втратою чи ні, і тому вважається в якості введення/виводу
додано Автор Russell Uhl, джерело

Більшість коментарів у вас є правильними, але я також викинув два мої центри (і перерахуйте коментарі тут):

Jonesy: "threading is most efficient in multi core environments" -> Yes, but this is a single core cpu...so I'll come back to this.

KooKiz і Джон Смібл: вони обидва згадують I/O. Ваша машина не хруснула разом на повній потужності 100% часу. Існує чимало інших речей, які забирають час, і під час цих подій ваш процесор отримує перерву.

(точка відліку: введення/виводу може бути мережевою передачею, читати жорсткий диск/RAM, запит SQL тощо. Все, що приносить нові дані до ЦП або завантажує дані з ЦП)

Ці переривання - це час, коли ваш процесор може робити інші речі. Якщо у вас є єдиний процесор (ми будемо ігнорувати hyperthreading на даний момент), і однопоточне додаток, то він працює щасливим, як це можливо. Однак це не відбувається постійно. Планування процесора дасть йому цикл або два, а потім переходите до чогось іншого, потім через деякий час поверніться до вашої програми, додайте ще кілька циклів, перемістіться і т. Д. Це дає ІЛЮСІОН можливість робити "кілька речі одночасно "на одному процесорі з одним ядром.

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

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

Зауважте, що це малоймовірно, що ви побачите прискорення 2: 1. Набагато імовірніше, що ваша 2-річна програма буде бачити лише 10-20% швидкості, якщо це буде. Пам'ятайте, що "інший" потік (який у будь-який момент - це те, що НЕ виконує введення/виведення) буде дійсно працювати лише на повну потужність, коли перша потока виконує введення/виведення.

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

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

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

3
додано
Я думав, що DRAM працює на замовлення десятків наносекунд . Десятки мілісекунд - затримка жорстких дисків.
додано Автор svick, джерело
Чи можлива пам'ять читання вважати введенням/виводом тут? Наскільки я знаю, це досить швидко, так що перемикач контекстів під час очікування читання не має сенсу (ймовірно, за винятком HyperThreading, але ви сказали, що ігнорувати це).
додано Автор svick, джерело
@svick: типова споживча пам'ять працює на якій, 7-10 мс? в такому періоді може трапитися багато циклів. Я чесно не знаю, чи відбудеться контекстний перемикач, але я б це підозрював. Відповідно до askldjd.wordpress.com/2011/02/20/length-of-the-thread-quantum , квант для цього конкретного процесора становить близько 5 мс ... що складає 13 мільйонів процесорних циклів. З огляду на ці цифри, я б припустив, що оперативна пам'ять вважається IO, оскільки ви можете відмовитися від 2 або більше повних значень просто, щоб отримати змінну.
додано Автор Russell Uhl, джерело
@svick: Вибачте, ви правильні. Тим не менш, ти все ще втрачає 13 тисяч циклів. У цьому випадку я не знаю, чи це вважається прийнятною втратою чи ні, і тому вважається в якості введення/виводу
додано Автор Russell Uhl, джерело

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

Приклад 1: нитки роблять це гіршим

Припустимо, що ми хотіли б провести два розрахунки, кожна з яких триватиме по 10 хвилин.

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

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

Приклад 2: нитки роблять це краще

Припустимо, що ми хотіли б провести два розрахунки. Мине хвилину, а іншу - 59 хвилин, , але ми не знаємо цього . (Пам'ятайте, що ми просто планувальник, який не розуміє коду.)

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

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

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

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

2
додано

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

Приклад 1: нитки роблять це гіршим

Припустимо, що ми хотіли б провести два розрахунки, кожна з яких триватиме по 10 хвилин.

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

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

Приклад 2: нитки роблять це краще

Припустимо, що ми хотіли б провести два розрахунки. Мине хвилину, а іншу - 59 хвилин, , але ми не знаємо цього . (Пам'ятайте, що ми просто планувальник, який не розуміє коду.)

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

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

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

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

2
додано

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

Але загальний час процесора складає лише половину історії ...

Рідинний інтерфейс

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

Непроцесорна обробка

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

ТПЛ

У випадку з C#, ви, ймовірно, хочете використовувати Паралельні бібліотеки завдань для паралельності (і для асинхронності ), а не намагатися самостійно керувати теми низького рівня. За замовчуванням заплановано Завдання пул потоку, уникаючи небезпеки надто багато потоків (і контекстних перемикачів).

Подивіться паралельне програмування з Microsoft. NET для отримання додаткової інформації.

0
додано

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

Але загальний час процесора складає лише половину історії ...

Рідинний інтерфейс

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

Непроцесорна обробка

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

ТПЛ

У випадку з C#, ви, ймовірно, хочете використовувати Паралельні бібліотеки завдань для паралельності (і для асинхронності ), а не намагатися самостійно керувати теми низького рівня. За замовчуванням заплановано Завдання пул потоку, уникаючи небезпеки надто багато потоків (і контекстних перемикачів).

Подивіться паралельне програмування з Microsoft. NET для отримання додаткової інформації.

0
додано

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

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

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

0
додано
var chat = new Chat();
var chat = new Chat();
642 учасників

Обсуждение вопросов по C# / .NET / .NET Core / .NET Standard / Azure Сообщества-организаторы: — @itkpi — @dncuug