асинхронний проти неблокуючий


373

Яка різниця між асинхронними та неблокуючими дзвінками? Також між блокуванням і синхронними дзвінками (будь ласка, з прикладами)?



1
Я добре зрозумів відмінності під час читання книги <Мережеве програмування Unix> Кол. 1, глава 6.
Бін

2
Цікава стаття: Підвищення продуктивності програми за допомогою асинхронного вводу / виводу . Він класифікує парадигми вводу / виводу на 4 категорії: (1) Блокування + Синхронність, (2) Неблокування + Синхронність, (3) Блокування + Асинхронність та (4) Неблокування + Асинхронність.
MS Dousti

@MSDousti Мені сказали, що це в чомусь неправильно, від експерта Google.
Рік

@MSDousti Після деякого дослідження, я думаю, що немає комбінацій (3) та (2), як ви описуєте в коментарях. Перевірте визначення Asynchronous, це говорить про те саме, що не блокує. Як ви бачите верхню відповідь, це підтверджує мою думку. Функція опитування та зворотного дзвінка - це лише способи / схеми реалізації асинхронної. Так, я кажу, що Блокування, Синхронний та Неблокуючий, Асинхронний - це 2 пари синоніма.
Рік

Відповіді:


304

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

Наприклад, у класичному API сокетів, неблокуючий сокет - це те, що просто негайно повертається за допомогою спеціального повідомлення про помилку "заблокує", тоді як блокуючий сокет був би заблокований. Ви повинні використовувати окрему функцію, таку як, selectабо pollз'ясувати, коли настав час для повторної спроби.

Але асинхронні сокети (як це підтримується в сокетах Windows), або асинхронний IO-шаблон, який використовується в .NET, зручніші. Ви викликаєте метод для запуску операції, і фреймворк відкликає вас, коли це буде зроблено. Навіть тут є основні відмінності. Асинхронні сокети Win32 "маршалюють" свої результати на певний потік GUI, передаючи повідомлення Window, тоді як .NET асинхронний IO є безкоштовним потоком (ви не знаєте, на який потік буде викликаний зворотний дзвінок).

Тому вони не завжди означають те саме. Для дестиляції прикладу сокета можна сказати:

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

готовий стан IO, а не стан завершення IO; на Linux см libaio
Will

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

2
Подальше запитання. Здається, що відповідь робить два різних відмінності між термінами. По-перше, сповіщення: якщо не блокується, програма має перевірити пізніше (опитування), тоді як асинхроніка означає, що ми можемо забути про неї та покластися на рамку / ОС, щоб повідомити нас за допомогою зворотного дзвінка або публікації події. По-друге, дія: незаблокування не робить нічого, окрім помилки, тоді як асинхронізація в черзі виконує дію або робить її у фоновому режимі. Яка різниця важливіша у розрізненні термінів? Чи будь-яке відмінність сильніше пов'язане з одним терміном? Або це неоднозначно?
Чад NB

2
@ChadNB - як термін, незаблокування сильно пов'язане з опитуванням. Що стосується питання про те, чи API пам’ятає вашу спробу викликати його: єдина причина, щоб API запам'ятався - це передзвонити вам. Якщо ви збираєтесь викликати його на повторне опитування, тоді вам вже доведеться підтримувати необхідний стан, щоб знати, щоб здійснити наступний виклик, тому API не додав би значення, також підтримуючи стан.
Даніель Ервікер

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

56

синхронний / асинхронний - це опис зв’язку між двома модулями.
блокування / неблокування полягає в описі ситуації одного модуля.

Приклад:
Модуль X: "Я".
Модуль Y: "книгарня".
X запитує Y: чи є у вас книга з назвою "c ++ праймер"?

1) блокування: перш ніж Y відповість X, X продовжує чекати відповіді. Тепер X (один модуль) блокується. X і Y - це два потоки або два процеси або одна нитка або один процес? ми не знаємо.

2) незаблокування: перш ніж Y відповість на X, X просто залишає там і робити інші речі. X може повертатися кожні дві хвилини, щоб перевірити, чи Y закінчив свою роботу? Або X не повернеться, поки Y не подзвонить йому? Ми не знаємо. Ми знаємо лише, що X може робити інші речі до того, як Y закінчить свою роботу. Тут X (один модуль) не блокує. X і Y - це два потоки або два процеси або один процес? ми не знаємо. Але ми впевнені, що X і Y не могли бути однією ниткою.

3) синхронний: перш ніж Y відповість X, X продовжує чекати відповіді. Це означає, що X не може продовжуватись, поки Y не закінчить свою роботу. Тепер ми говоримо: X і Y (два модулі) синхронні. X і Y - це два потоки або два процеси або одна нитка або один процес? ми не знаємо.

4) асинхронний: перш ніж Y відповість X, X залишає там і X може виконувати інші завдання. X не повернеться, поки Y не зателефонує йому. Тепер ми кажемо: X і Y (два модулі) асинхронні. X і Y - це два потоки або два процеси або один процес? ми не знаємо. Але ми впевнені, що X і Y не могли бути однією ниткою.


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

Ось типовий приклад про незаблокування та синхронність:

// thread X
while (true)
{
    msg = recv(Y, NON_BLOCKING_FLAG);
    if (msg is not empty)
    {
        break;
    }
    sleep(2000); // 2 sec
}

// thread Y
// prepare the book for X
send(X, book);

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

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

Наприклад, ми можемо розробити такий тип архітектури:

// Module X = Module X1 + Module X2
// Module X1
while (true)
{
    msg = recv(many_other_modules, NON_BLOCKING_FLAG);
    if (msg is not null)
    {
        if (msg == "done")
        {
            break;
        }
        // create a thread to process msg
    }
    sleep(2000); // 2 sec
}
// Module X2
broadcast("I got the book from Y");


// Module Y
// prepare the book for X
send(X, book);

У прикладі тут ми можемо сказати це

  • X1 не блокує
  • X1 і X2 є синхронними
  • X і Y асинхронні

Якщо вам потрібно, ви також можете описати ці теми, створені в X1, чотирма словами.

Більш важливі речі: коли ми використовуємо синхронні замість асинхронних? коли ми використовуємо блокування замість блокування?

Чому Nginx не блокує? Чому Apache блокується?

Щоб зробити хороший вибір, ви повинні проаналізувати свої потреби та перевірити працездатність різних архітектур. Не існує такої архітектури, яка підходила б для різних потреб.


7
ІМО найкраща відповідь, оскільки вона вловить суть концепції: відносини між одним або двома учасниками.
Фабіо

і в 1, і в 3 Y виступає як обмежений ресурс. Більше немає Y, щоб допомогти X
UVM

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

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

Що ж, так, точні формулювання ми можемо стверджувати цілий день :)
Микола Фетисов

але як ви поясніть AIO в Linux? який використовував як Async, так і неблокуючі. AIO LINKS
Djvu

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

2
Async не обов'язково означає, що це робиться паралельно, дивіться цей чудовий пост у stackoverflow про паралельне та паралельне програмування.
BARJ

17

Ставлячи це питання в контексті NIO та NIO.2 в java 7, async IO є на один крок більш досконалим, ніж неблокуючий. Якщо не блокує дзвінки Java NIO, ви можете встановити всі канали (SocketChannel, ServerSocketChannel, FileChannel тощо) як такі AbstractSelectableChannel.configureBlocking(false). Після повернення цих викликів вводу-виводу, однак, вам, швидше за все, потрібно буде контролювати чеки, такі як, якщо і коли читати / писати знову та ін.
Наприклад,

while (!isDataEnough()) {
    socketchannel.read(inputBuffer);
    // do something else and then read again
}

За допомогою асинхронної api в java 7 ці елементи керування можна зробити більш універсальними. Один з 2 способів - це використання CompletionHandler. Зауважте, що обидва readдзвінки не блокують.

asyncsocket.read(inputBuffer, 60, TimeUnit.SECONDS /* 60 secs for timeout */, 
    new CompletionHandler<Integer, Object>() {
        public void completed(Integer result, Object attachment) {...}  
        public void failed(Throwable e, Object attachment) {...}
    }
}

3
FileChannelне вибирається і не може бути налаштований на неблокування.
michaelliu

15

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

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

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

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


1
+1 Хороша відповідь. Люди повинні усвідомлювати, що "асинхронний" може означати або неблокуючий, або підхід Microsoft до асинхронного подій (на основі подій / зворотного виклику).
Інженер

14

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

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


неблокуючий результат взагалі нічого не повертає
генеральний директор в Апартіко

9

Неблокуючий: Ця функція не буде чекати, поки знаходиться у стеці.

Асинхронний: робота може продовжуватися від імені виклику функції після того, як цей виклик залишив стек


1
@Marenz - це означає, що ви не можете робити незаблокування io безпосередньо за допомогою викликів posix. Це не змінює значення, яке він надає тут.
tmc

@Marenz Що означає, що прапор ігнорується для файлів. Це не впливає на значення цієї відповіді.
Маркіз Лорн

8

Синхронність визначається як те, що відбувається одночасно.

Асинхронність визначається як не відбувається одночасно.

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

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

Найпростіше рішення відоме як блокування.

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

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

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

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

Неблокування - це коли ви вирішите робити інші непов'язані речі, поки ви чекаєте операції. Перевірка наявності відповіді, як вважаєте за потрібне.

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

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

Тут йде асинхронність.

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

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

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

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

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

І ще одне - вам потрібно усвідомити компроміси, які всі три забезпечують. Один явно не кращий за інших. Подумайте на моєму прикладі. Якщо ваш тостер настільки швидкий, ви не встигнете випрати посуд, навіть не почнете її мити, так швидко ваш тостер. Почати щось у цьому випадку - це лише марна трата часу та сил. Блокування буде робити. Так само, якщо миття посуду займе в 10 разів більше часу, ніж тост. Ви повинні запитати себе, що важливіше зробити? Тост може наступити холодно і важко до того часу, не варто, блокування також зробиться. Або вам слід підбирати швидші речі, поки ви чекаєте. Існує більш очевидно, але моя відповідь вже досить довгий, моя думка полягає в тому, що вам потрібно подумати над усім цим, і над складностями впровадження кожного, щоб вирішити, чи варто його, і чи це '

Редагувати:

Незважаючи на те, що це вже давно, я також хочу, щоб це було повно, тому я додам ще два бали.

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

У нашому прикладі це було б як запустити тостер, потім посудомийну машину, потім мікрохвильову піч тощо. А потім чекати на будь-яку з них. Де ви б перевірили тостер, щоб побачити, чи зроблено це, якщо ні, ви знову перевірите посудомийну машину, якщо ні, мікрохвильову піч тощо.

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

Я не дуже розумію, як ми туди потрапили. Але якщо мова йде про IO та Computation, синхронні та асинхронні часто посилаються на те, що краще відомо як «неперекрите» та «перекрите». Тобто, асинхронність означає, що IO та обчислення перекриваються, так само, що відбуваються одночасно. У той час як синхронних засобів їх немає, таким чином відбувається послідовно. Для синхронного неблокування це означатиме, що ви не запускаєте інший IO або Computation, ви просто зайняті в очікуванні та імітуєте виклик блокування. Я б хотів, щоб люди перестали зловживати подібними синхронними та асинхронними. Тож я не заохочую це.


Не знаєте, чому ви сказали: "Синхронність визначається як те, що відбувається одночасно". Вся ідея полягає в тому, що це не є одночасно, але не відбувається одночасно.
Хелсінг

Це була чудова аналогія! Ви просто підсмажили!
d-coder

@Helsing Це буквально те, що означає це слово. Синхронний означає однаковий час, а асинхронний означає не один і той же час: p. Причина, що щось асинхронна, полягає в тому, що воно не може статися одночасно, воно повинно відбуватися до або після. Якщо це може статися одночасно, ви можете просто паралелізувати це чи зробити це в будь-якому порядку, і вам не знадобиться явна синхронізація. Ось чому асинхронне програмування - це все, щоб зробити це, потім це, зачекайте на ці речі, а потім і т. Д. Тому що жодне з цих речей не може відбуватися разом одночасно.
Дідьє А.

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

3

Блокування виклику: управління повертається лише тоді, коли виклик завершується.

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


Синхронна програма: програма, яка використовує блокування дзвінків. Щоб не замерзнути під час виклику, у нього повинно бути 2 або більше потоку (саме тому він називається Synchronous - потоки працюють синхронно).

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


1
Неблокуючий виклик: управління повертається після того, як зробить стільки, скільки можна зробити по суті негайно; метод вказує, скільки було зроблено. Це відрізняється від асинхронного дзвінка, який веде себе так, як ви описали для блокування виклику.
supercat

0

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


0

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


-2

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

Не блокуючи: контроль повертається до процесу одразу після виклику


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