Яке значення терміна "безпечний для потоків"?


367

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


8
Щойно побачив тут цікаву дискусію з цього приводу: blogs.msdn.com/ericlippert/archive/2009/10/19/…
Себастьян

Відповіді:


256

З Вікіпедії:

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

Є кілька способів досягти безпеки ниток:

Повторне працевлаштування:

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

Взаємовиключення:

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

Місцеве сховище потоків:

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

Атомні операції:

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

читати далі:

http://en.wikipedia.org/wiki/Thread_safety



4
У технічному зв’язку відсутнє кілька критичних моментів. Розташована пам'ять, про яку йде мова, повинна бути зміненою ((Пам'ять лише для читання не може бути потоковою, Небезпечна), а кілька потоків повинні: a) виконувати кілька операцій запису на пам'яті, в середині яких пам'ять знаходиться в непослідовному (неправильному) стан, і b) дозволяють іншим потокам переривати цей потік, поки пам'ять не відповідає.
Чарльз Бретана

20
коли пошук Google першим результатом є вікі, немає жодного сенсу робити його зайвим.
Ранвір

Що ви маєте на увазі "код, що отримує доступ до функції"? Сама функція, що виконується, - це код, чи не так?
Корай Тугай

82

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

http://mindprod.com/jgloss/threadsafe.html


34
В рамках того ж процесу!
Алі Афшар

Дійсно, в тому ж процесі :)
Марек Блотний

4
"Написання коду, який стабільно працюватиме тижнями, вимагає надзвичайної параної". Це цитата, яка мені подобається :)
Jim T

5
да! ця відповідь просто повторює питання! --- І чому тільки в межах одного процесу ??? Якщо код виходить з ладу, коли кілька потоків виконують його з різних процесів, то, можливо, ("спільна пам'ять" може бути у файлі диска), він НЕ безпечний для потоків !!
Чарльз Бретана

1
@ mg30rg. Можливо, плутанина є результатом якихось думок, що коли блок коду виконується за допомогою декількох процесів, але лише однією ниткою в процесі, що це, якимось чином, є сценарієм "однопоточний", а не багатопотоковим сценарієм . Ця думка навіть не помилкова. Це просто неправильне визначення. Зрозуміло, що декілька процесів, як правило, не виконуються в одному потоці синхронізовано (за винятком рідкісних сценаріїв, коли процеси за дизайном узгоджуються один з одним і ОС розділяє потоки між процесами)
Чарльз Бретана

50

Більш інформативним є питання про те, що робить код не потоковим, а відповідь - це чотири умови, які повинні бути правдивими ... Уявіть собі наступний код (і це машинний переклад мови)

totalRequests = totalRequests + 1
MOV EAX, [totalRequests]   // load memory for tot Requests into register
INC EAX                    // update register
MOV [totalRequests], EAX   // store updated value back to memory
  1. Перша умова полягає в тому, що є місця пам'яті, які доступні з більш ніж одного потоку. Зазвичай ці локації є глобальними / статичними змінними або доступні в пам'яті купи з глобальних / статичних змінних. Кожен потік отримує власний фрейм стека для локальних змінних, що визначаються функцією / методом, тому ці локальні змінні функції / методу, отох (які є у стеку) доступні лише з одного потоку, який володіє цим стеком.
  2. Друга умова полягає в тому, що для правильної роботи програми існує властивість (часто її називають інваріантною ), яка пов'язана з цими місцями спільної пам'яті, яка повинна бути істинною, або дійсною. У наведеному вище прикладі властивість полягає в тому, що " totalRequests повинен точно представляти загальну кількість разів, коли будь-який потік виконував будь-яку частину оператора збільшення ". Як правило, це інваріантне властивість має бути виконане істинним (у цьому випадку totalRequests повинен мати точний підрахунок), перш ніж відбудеться оновлення, щоб оновлення було правильним.
  3. Третя умова полягає в тому, що інваріантна властивість НЕ зберігається протягом деякої частини фактичного оновлення. (Це тимчасово недійсне або помилкове протягом певної частини обробки). У цьому конкретному випадку, з моменту отримання загальних запитів до моменту збереження оновленого значення, totalRequests не задовольняє інваріант.
  4. Четверта і остання умова, яка повинна мати місце для перегонів (і щоб код НЕ був таким "безпечним для потоків"), полягає в тому, що інший потік повинен мати можливість доступу до спільної пам'яті, поки інваріант розірваний, тим самим спричиняючи непослідовність або неправильна поведінка.

6
Це стосується лише того, що відомо як перегони даних , і, звичайно, важливо. Тим не менш, є й інші способи, як код не може бути безпечним для потоку - наприклад, неправильне блокування, яке може призвести до тупикових ситуацій. Навіть щось таке, як виклик System.exit () десь у потоці Java, робить цей код не безпечним.
Інго

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

1
Але зауважте, що тупик не відбудеться при запуску однопотокових, тому для більшості з нас це, безумовно, підпадає під інтуїтивне значення (не) "потоку безпечно".
Джон Кумбс

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

34

Мені подобається визначення програми Java Concurrency Java Brac Goetz у практиці за її всебічність

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


Це визначення є неповним і не конкретним, і, безумовно, не є вичерпним. Скільки разів він повинен безпечно працювати, Лише один раз? десять разів? кожного разу? 80% часу? і не вказується, що робить його "небезпечним". Якщо вона не запускається безпечно, але помилка сталася через те, що є помилка поділу на нуль, чи це робить її ниткою - "Небезпечною"?
Чарльз Бретана

Будьте більш цивільними наступного разу, і, можливо, ми зможемо обговорити. Це не Reddit, і я не в настрої спілкуватися з грубими людьми.
Buu Nguyen

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

28

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

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

Наприклад, у Java є два класи, які майже еквівалентні, StringBufferі StringBuilder. Різниця полягає в тому, що StringBufferбезпечно для потоків, тому один примірник StringBufferможе використовуватись декількома потоками одночасно. StringBuilderне є безпечним для потоків і розроблений як заміна більш високої продуктивності для тих випадків (переважна більшість), коли String будується лише одним потоком.


22

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


21

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

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

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


9

Так і ні.

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

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


9

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

Мені подобається визначення, яке дав Java Concurrency в практиці :

[Частина коду] є безпечною для потоків, якщо вона веде себе правильно при доступі з декількох потоків, незалежно від планування чи переплетення виконання цих потоків середовищем виконання та без додаткової синхронізації чи іншої координації з боку код виклику.

При правильно вони означають , що програма веде себе відповідно до своїх вимог.

Надуманий приклад

Уявіть, що ви реалізуєте лічильник. Можна сказати, що він поводиться правильно, якщо:

  • counter.next() ніколи не повертає значення, яке вже було повернено раніше (для простоти ми вважаємо, що немає переповнення тощо)
  • всі значення від 0 до поточного значення на певному етапі повертаються (жодне значення не пропускається)

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

Примітка: перехресне повідомлення програмистів



5

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

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


5

Я хотів би додати ще трохи інформації на додаток до інших хороших відповідей.

Безпека нитки передбачає, що кілька потоків можуть записувати / читати дані в один об’єкт без помилок невідповідності пам'яті. У дуже багатопотоковій програмі безпечна програма для потоку не викликає побічних ефектів для спільних даних .

Перегляньте це питання SE для більш детальної інформації:

Що означає безпека ниток?

Програма безпечної нитки гарантує послідовність пам’яті .

З сторінки документації Oracle на розширеному сумісному API:

Властивості послідовності пам'яті:

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

synchronizedІ volatileконструкції, а також Thread.start()і Thread.join()методи, форма може відбувається, перш за , ніж відносини.

Методи всіх класів у java.util.concurrentта його підпакетів поширюють ці гарантії на синхронізацію вищого рівня. Зокрема:

  1. Дії в потоці перед розміщенням об'єкта в будь-якій паралельній колекції відбуваються перед діями, наступними після доступу або видалення цього елемента з колекції в іншому потоці.
  2. Дії в потоці до подання події Runnableдо того, як Executorвідбудеться, до початку її виконання. Аналогічно для Callables, поданий до ExecutorService.
  3. Дії, здійснені асинхронним обчисленням, представлені Futureдіями, що відбуваються раніше, після отримання результату через Future.get()інший потік.
  4. Дії перед "вивільненням" методів синхронізатора, таких як дії, що Lock.unlock, Semaphore.release, and CountDownLatch.countDownвідбудуться перед успішним методом "придбання", наприклад, Lock.lock, Semaphore.acquire, Condition.await, and CountDownLatch.awaitнад тим самим об'єктом синхронізатора в іншому потоці.
  5. Для кожної пари потоків, які успішно обмінюються об'єктами через,, Exchangerдії, що передують перед exchange()кожним потоком, відбуваються раніше, ніж ті, що слідують за відповідним обміном () в іншому потоці.
  6. Дії до виклику CyclicBarrier.awaitта Phaser.awaitAdvance(як і його варіанти) відбуваються перед діями, виконаними бар'єрною дією, і дії, що виконуються бар'єрною дією, відбуваються перед тим, як дії, наступні після успішного повернення з відповідних, очікують в інших потоках.

4

Щоб заповнити інші відповіді:

Синхронізація викликає занепокоєння, коли код у вашому методі робить одну з двох речей:

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

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

Планування ниток не гарантується як круглообертове . Завдання може повністю зависати ЦП за рахунок ниток того самого пріоритету. Ви можете використовувати Thread.yield (), щоб мати совість. Ви можете використовувати (в java) Thread.setPriority (Thread.NORM_PRIORITY-1), щоб знизити пріоритет потоку

Плюс остерігайтеся:

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

2

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

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


2

Відповімо на це на прикладі:

class NonThreadSafe {

    private int counter = 0;

    public boolean countTo10() {
        count = count + 1;
        return (count == 10);
    }

countTo10Метод додає один до прилавка , а потім повертає істину , якщо число досягло 10. Він повинен повертати тільки істинний раз.

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

Наприклад, якщо число починається з 9, один потік може додати 1 для підрахунку (10), але тоді другий потік може ввести метод і знову додати 1 (що робить 11), перш ніж перший потік має шанс виконати порівняння з 10 Тоді обидві нитки роблять порівняння і виявляють, що кількість дорівнює 11, і жодна з них не повертає істину.

Тож цей код не є безпечним для потоків.

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

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


1

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

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

І саме тому деякі люди вважають за краще використовувати термін внутрішньо синхронізований .

Термінологічні набори

Є три основні набори термінології для цих ідей, з якими я стикався. Перший та історично популярніший (але гірший):

  1. нитка безпечна
  2. не безпечно для ниток

Другий (і кращий):

  1. різьблення
  2. нитка сумісна
  3. нитка ворожа

Третя частина:

  1. внутрішньо синхронізовані
  2. зовнішньо синхронізовані
  3. несинхронізується

Аналогії

потоковий безпечний ~ потоковий захист ~ внутрішньо синхронізований

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

не є безпечним для потоків (але приємно) ~ сумісний з потоком ~ зовнішньо синхронізований ~ вільно-потоковий

Припустимо, ви йдете в банк. Існує лінія, тобто суперечка для банківських касирів. Оскільки ти не дикун, ти визнаєш, що найкраще робити в розпал суперечок за ресурс - це чергуватись як цивілізована істота. Ніхто технічно вас не змушує цього робити. Ми сподіваємось, що у вас є необхідне соціальне програмування, щоб зробити це самостійно. У цьому сенсі лобі банку зовні синхронізоване. Чи варто говорити, що це небезпечно для ниток? це те, що мається на увазі, якщо ви йдете з потокобезпечна , поточно-небезпечних біполярним набором термінології. Це не дуже вдалий набір термінів. Краща термінологія зовнішньо синхронізована,Лобі банку не є ворожим до доступу до нього декількох клієнтів, але це також не виконує роботу з їх синхронізацією. Замовники роблять це самі.

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

не безпечно для потоків (і погано) ~ нитка ворожа ~ несинхронізація

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

Чому безпечні нитки та ін. - це погана термінологія

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

ПРИМІТКА: Багато посібників із програмного забезпечення насправді використовують термін "безпечний для потоків" для позначення "сумісний з потоками", додаючи ще більше плутанини тому, що вже було безладно! Я уникаю термінів "безпечно для потоків" та "безпечно для потоків" за будь-яку ціну саме з цієї причини, оскільки одні джерела називатимуть щось "безпечним для потоків", а інші називають "безпечним для потоку", оскільки вони не можуть погодитися щодо того, чи потрібно вам відповідати деяким додатковим стандартам безпеки (примітиви синхронізації), або просто НЕ вороже вважатись "безпечним". Тому уникайте цих термінів і використовуйте натомість розумніші терміни, щоб уникнути небезпечних помилок з іншими інженерами.

Нагадування про наші цілі

По суті, наша мета - підірвати хаос.

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

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


1

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

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

Що ще цікавіше, деякі колекції хеш-наборів, такі як оригінальна не-загальна в .NET, можуть гарантувати, що доки жоден елемент не буде видалений, і за умови, що до них пишеться лише один потік, будь-який потік, який намагається читання колекції буде вести себе як би доступ до колекції, де оновлення можуть затримуватися і відбуватись у довільному порядку, але в іншому випадку вони будуть вести себе нормально. Якщо потік №1 додає X, а потім Y, а нитка №2 шукає і бачить Y, а потім X, для потоку №2 можна було б побачити, що Y існує, але X немає; чи така поведінка є "безпечною для потоків" чи ні, залежатиме від того, чи готовий нитка №2 боротися з цією можливістю.

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


0

Найпростішими словами: P Якщо безпечно виконувати кілька потоків на блоці коду, він є безпечним для потоків *

* діють умови

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

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