Каква е разликата между асинхронни и неблокиращи повиквания? Също така между блокиране и синхронни повиквания (с примери, моля)?
асинхронни и неблокиращи повиквания? също между блокиращ и синхронен
Отговори (14)
При много обстоятелства те са различни имена за едно и също нещо, но в някои контексти са доста различни. Така че зависи. Терминологията не се прилага по напълно последователен начин в цялата софтуерна индустрия.
Например в API на класическите сокети, неблокиращият сокет е този, който просто се връща незабавно със специално съобщение за грешка „ще блокира“, докато блокиращият сокет би блокирал. Трябва да използвате отделна функция като select
или poll
, за да разберете кога е подходящото време за повторен опит.
Но асинхронните сокети (поддържани от Windows сокети) или асинхронният IO модел, използван в .NET, са по-удобни. Извиквате метод, за да стартирате операция, и рамката ви извиква обратно, когато тя приключи. Дори тук има основни разлики. Асинхронните Win32 сокети „маршалират“ своите резултати към конкретна GUI нишка чрез предаване на съобщения на Window, докато .NET асинхронната IO е свободна нишка (не знаете към коя нишка ще бъде извикано вашето обратно извикване).
Така че те не винаги означават едно и също нещо. За да обобщим примера за сокет, можем да кажем:
- Блокирането и синхронното означават едно и също нещо: извиквате API, той прекъсва нишката, докато получи някакъв отговор и ви го върне.
- Неблокирането означава, че ако отговорът не може да бъде върнат бързо, API се връща незабавно с грешка и не прави нищо друго. Така че трябва да има някакъв свързан начин за запитване дали API е готов за извикване (т.е. да се симулира изчакване по ефективен начин, за да се избегне ръчно запитване в тесен цикъл).
- Асинхронно означава, че API винаги се връща незабавно, след като е започнал „фоново“ усилие за изпълнение на вашата заявка, така че трябва да има някакъв свързан начин за получаване на резултата.
синхронен / асинхронен е да опише връзката между два модула.
блокиращ / неблокиращ е да опише ситуацията на един модул.
Пример:
Модул X: I.
Модул Y: книжарница.
X пита Y: имате ли книга с име c++ primer?
блокиране: преди Y да отговори на X, X продължава да чака там за отговор. Сега X (един модул) блокира. X и Y са две нишки или два процеса или една нишка или един процес? ние НЕ ЗНАЕМ.
без блокиране: преди Y да отговори на X, X просто си тръгва и прави други неща. X може да се връща на всеки две минути, за да провери дали Y е свършил работата си? Или X няма да се върне, докато Y не му се обади? Не знаем. Знаем само, че X може да прави други неща, преди Y да свърши работата си. Тук X (един модул) е неблокиращ. X и Y са две нишки или два процеса или един процес? ние НЕ ЗНАЕМ. НО сме сигурни, че X и Y не могат да бъдат една нишка.
синхронно: преди Y да отговори на X, X продължава да чака там за отговора. Това означава, че X не може да продължи, докато Y не завърши работата си. Сега казваме: X и Y (два модула) са синхронни. X и Y са две нишки или два процеса или една нишка или един процес? ние НЕ ЗНАЕМ.
асинхронен: преди 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;
}
else
{
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
}
else
{
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 с четирите думи.
По-важните неща са: кога използваме синхронно вместо асинхронно? кога използваме блокиране вместо неблокиране? Правенето на блокиране на X1 по-добро ли е от неблокиране? По-добре ли е да направите X и Y синхронни от асинхронни? Защо Nginx не блокира? Защо Apache блокира? Тези въпроси са това, което трябва да разберете.
За да направите добър избор, трябва да анализирате нуждите си и да тествате производителността на различни архитектури. Няма такава архитектура, която да е подходяща за различни нужди.
- Асинхронно се отнася до нещо, направено паралелно, да речем е друга нишка.
- Неблокиране често се отнася до проучване, т.е. проверка дали даденото условие се изпълнява (сокетът е четим, устройството има повече данни и т.н.)
Синхронно се определя като случващо се по едно и също време.
Асинхронен се определя като не се случва едновременно.
Това е причината за първото объркване. Синхронното всъщност е това, което е известно като паралелно. Докато асинхронното е последователно, направете това, след това направете онова.
Сега целият проблем е около моделирането на асинхронно поведение, защото имате някаква операция, която се нуждае от отговора на друга, преди да започне. Следователно това е проблем с координацията, как ще разберете, че сега можете да започнете тази операция?
Най-простото решение е известно като блокиране.
Блокирането е, когато просто изберете да изчакате другото нещо да бъде направено и да ви върне отговор, преди да преминете към операцията, която го е изисквала.
Така че, ако трябва да сложите масло върху препечен хляб, и по този начин първо трябва да препечете отгледаното. Начинът, по който бихте ги координирали, е, че първо препичате хляба, след това се взирате безкрайно в тостера, докато извади тоста, и след това ще продължите да ги слагате с масло.
Това е най-простото решение и работи много добре. Няма истинска причина да не го използвате, освен ако случайно имате и други неща, които трябва да правите, които не изискват координация с операциите. Например, миене на някои ястия. Защо да чакате бездействайки, зяпайки непрекъснато тостера, докато тостът изскочи, когато знаете, че ще отнеме малко време и можете да измиете цяла чиния, докато свърши?
Това е мястото, където влизат в действие две други решения, известни съответно като неблокиращо и асинхронно.
Неблокиране е, когато изберете да правите други несвързани неща, докато чакате операцията да бъде извършена. Проверете отново наличността на отговора, както сметнете за добре.
Така че вместо да гледате тостера, за да изскочи. Отиваш и измиваш цяла чиния. И тогава надничате в тостера, за да видите дали тостовете са изскочили. Ако не са, отивате да измиете друг съд, като проверявате тостера между всяко ястие. Когато видите, че тостовете са изскочили, спирате да миете чиниите, а вместо това взимате тоста и продължавате да ги намазвате с масло.
Постоянната проверка на тостовете може да е досадна, представете си, че тостерът е в друга стая. Между ястията си губите времето, отивайки в другата стая, за да проверите препечения хляб.
Тук идва асинхронното.
Асинхронен е, когато изберете да правите други несвързани неща, докато чакате операцията да бъде извършена. Вместо да го проверявате обаче, вие делегирате работата по проверката на нещо друго, може да бъде самата операция или наблюдател, и имате това нещо да ви уведомява и евентуално да ви прекъсва, когато отговорът е наличен, за да можете да продължите към другата операция, която имах нужда от него.
Това е странна терминология. Няма много смисъл, тъй като всички тези решения са начини за създаване на асинхронна координация на зависими задачи. Ето защо предпочитам да го наричам събитие.
Така че за този решавате да надстроите своя тостер, така че да издава звуков сигнал, когато тостовете са готови. Случва се постоянно да слушате, дори докато миете чинии. Когато чуете звуковия сигнал, вие се нареждате на опашка в паметта си, че веднага щом приключите с миенето на текущия си съд, ще спрете и ще отидете да сложите масло върху препечения хляб. Или можете да изберете да прекъснете миенето на текущото ястие и да се заемете с тоста веднага.
Ако имате проблеми с чуването на звуковия сигнал, можете да накарате партньора си да гледа тостера вместо вас и да дойде да ви каже кога тостът е готов. Вашият партньор може сам да избере някоя от горните три стратегии, за да координира задачата си да наблюдава тостера и да ви казва кога е готов.
И накрая, добре е да разберете, че докато неблокиращите и асинхронните (или това, което предпочитам да наричам събитийни) ви позволяват да правите други неща, докато чакате, вие също не го правите. Можете да изберете непрекъснато да проверявате състоянието на неблокиращо повикване, без да правите нищо друго. Това обаче често е по-лошо от блокирането (като гледане на тостера, след това настрани, после обратно към него, докато не приключи), така че много неблокиращи API ви позволяват да преминете към режим на блокиране от него. За evented можете просто да изчакате неактивен, докато не бъдете уведомени. Недостатъкът в този случай е, че добавянето на известието беше сложно и потенциално скъпо в началото. Трябваше да си купите нов тостер с функция за звуков сигнал или да убедите партньора си да го гледа вместо вас.
И още нещо, трябва да осъзнаете компромисите, които и трите предоставят. Едно не е очевидно по-добро от другите. Помислете за моя пример. Ако вашият тостер е толкова бърз, няма да имате време да измиете чиния, дори да не започнете да го миете, ето колко бърз е вашият тостер. В този случай започването на нещо друго е просто загуба на време и усилия. Блокирането ще свърши работа. По същия начин, ако миенето на чиния ще отнеме 10 пъти повече от препичането. Трябва да се запитате какво е по-важно да свършите? Тостът може да стане студен и твърд по това време, не си струва, блокирането също ще свърши работа. Или трябва да изберете по-бързи неща за вършене, докато чакате. Има и по-очевидно, но отговорът ми вече е доста дълъг, искам да кажа, че трябва да помислите за всичко това и сложността на внедряването на всеки, за да решите дали си заслужава и дали наистина ще подобри вашата пропускателна способност или производителност.
Редактиране:
Въпреки че това вече е дълго, аз също искам да е пълно, така че ще добавя още две точки.
- Обикновено съществува и четвърти модел, известен като мултиплексиран. Това е, когато докато чакате една задача, стартирате друга и докато чакате и двете, стартирате още една и така нататък, докато имате много задачи, всички стартирани и след това чакате бездействащи, но на всички тях. Така че веднага щом някой бъде готов, можете да продължите с обработката на отговора му и след това да се върнете към изчакване на останалите. Известно е като мултиплексирано, защото докато чакате, трябва да проверявате всяка задача една след друга, за да видите дали са изпълнени ad vitam, докато една не стане. Това е малко разширение в допълнение към нормалното неблокиране.
В нашия пример би било като да стартирате тостера, след това съдомиялната машина, след това микровълновата печка и т.н. И след това да изчакате някой от тях. Където бихте проверили тостера, за да видите дали е готов, ако не, бихте проверили съдомиялната машина, ако не, микровълновата печка и отново наоколо.
- Въпреки че вярвам, че е голяма грешка, синхронният често се използва за означаване на едно нещо в даден момент. И асинхронни много неща наведнъж. Така ще видите синхронно блокиране и неблокиране, използвани за обозначаване на блокиране и неблокиране. И асинхронно блокиране и неблокиране, използвани за обозначаване на мултиплексирани и събития.
Наистина не разбирам как стигнахме до там. Но когато става въпрос за IO и изчисления, синхронните и асинхронните често се отнасят до това, което е по-известно като неприпокриване и припокриване. Това означава, че асинхронно означава, че IO и изчислението се припокриват, т.е. се случват едновременно. Докато синхронни означава, че не са, като по този начин се случват последователно. За синхронно неблокиране това би означавало, че не стартирате друг IO или изчисление, просто чакате заето и симулирате блокиращо повикване. Иска ми се хората да спрат да злоупотребяват със синхронни и асинхронни по този начин. Така че не го насърчавам.
Поставяйки този въпрос в контекста на NIO и NIO.2 в Java 7, async IO е една стъпка по-напреднал от неблокирането. С java NIO неблокиращи повиквания, човек би задал всички канали (SocketChannel, ServerSocketChannel, FileChannel и т.н.) като такива чрез извикване на AbstractSelectableChannel.configureBlocking(false)
. След като тези IO извиквания се върнат обаче, вероятно все още ще трябва да контролирате проверките, като например дали и кога да четете/пишете отново и т.н.
Например,
while (!isDataEnough()) {
socketchannel.read(inputBuffer);
// do something else and then read again
}
С асинхронния API в Java 7 тези контроли могат да бъдат направени по по-гъвкави начини. Един от двата начина е да използвате 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) {...}
}
}
FileChannel
не може да се избира и не може да се конфигурира да не блокира.
- person michaelliu; 14.03.2014
Както вероятно можете да видите от множеството различни (и често взаимно изключващи се) отговори, зависи от това кого питате. В някои арени термините са синоними. Или всеки може да се отнася до две подобни концепции:
- Едно тълкуване е, че повикването ще направи нещо във фонов режим по същество без надзор, за да позволи на програмата да не бъде задържана от дълъг процес, който не е необходимо да контролира. Възпроизвеждането на аудио може да бъде пример - програма може да извика функция за възпроизвеждане (да речем) на mp3 и от този момент нататък може да продължи към други неща, като оставя на операционната система да управлява процеса на рендиране на аудиото на звуковия хардуер .
- Алтернативното тълкуване е, че повикването ще направи нещо, което програмата ще трябва да наблюдава, но ще позволи по-голямата част от процеса да се случи във фонов режим, като само уведомява програмата в критични точки от процеса. Например, асинхронният файлов IO може да бъде пример - програмата предоставя буфер на операционната система за запис във файл, а операционната система уведомява програмата само когато операцията е завършена или възникне грешка.
И в двата случая намерението е да се позволи на програмата да не бъде блокирана в очакване за завършване на бавен процес - как се очаква програмата да реагира е единствената истинска разлика. Кой термин се отнася за кой също се променя от програмист на програмист, език на език или платформа на платформа. Или термините може да се отнасят до напълно различни концепции (като използването на синхронен/асинхронен във връзка с програмирането на нишки).
Съжалявам, но не вярвам, че има единствен правилен отговор, който да е глобално верен.
Неблокиращото повикване се връща незабавно с всички налични данни: пълния брой заявени байтове, по-малко или никакви.
Едно асинхронно повикване изисква прехвърляне, което ще бъде извършено изцяло (изцяло), но ще завърши в бъдеще време.
Блокиране на повикване: Контролът се връща само когато повикването завърши.
Неблокиращо обаждане: Контролът се връща незабавно. По-късната ОС по някакъв начин уведомява процеса, че повикването е завършено.
Синхронна програма: Програма, която използва Блокиращи повиквания. За да не замръзне по време на разговора, той трябва да има 2 или повече нишки (затова се нарича Synchronous - нишките се изпълняват синхронно).
Асинхронна програма: Програма, която използва неблокиращи повиквания. Може да има само 1 нишка и пак да остане интерактивна.
Неблокиране: Тази функция няма да чака, докато е в стека.
Асинхронно: Работата може да продължи от името на извикването на функция, след като това извикване е напуснало стека
Синхронно означава да започнете един след резултата на другия, в последователност.
Асинхронен означава стартиране заедно, не е гарантирана последователност в резултата
Блокиране означава нещо, което причинява пречка за извършване на следващата стъпка.
Неблокиране означава нещо, което продължава да работи, без да чака нищо, преодолявайки препятствието.
Блокиране напр.: Почуквам на вратата и чакам, докато я отворят. (бездействам тук)
Неблокиране напр.: Почуквам на вратата, ако я отворят мигновено, поздравявам ги, влизам вътре и т.н. Ако не отворят мигновено, отивам до следващата къща и почуквам на нея . (Правя едно или друго, не бездействам)
Синхронен например: Ще изляза само ако вали. (съществува зависимост)
Асинхронно например: Ще изляза. Може да вали. (независими събития, няма значение кога се случват)
Синхронен или асинхронен, и двата могат да бъдат блокиращи или неблокиращи и обратно
Блокиращите модели изискват иницииращото приложение да блокира, когато I/O е започнало. Това означава, че не е възможно обработката и I/O да се припокриват едновременно. Синхронният неблокиращ модел позволява припокриване на обработка и I/O, но изисква приложението да проверява състоянието на I/O на повтаряща се основа. Това оставя асинхронен неблокиращ I/O, което позволява припокриване на обработка и I/O, включително известяване за завършване на I/O.
Най-просто казано,
function sum(a,b){
return a+b;
}
е неблокиращ. докато Asynchronous се използва за изпълнение на блокираща задача и след това връща нейния отговор
Те се различават само по правописа. Няма разлика в това, за което се отнасят. За да бъдем технически, можете да кажете, че те се различават по акцент. Неблокирането се отнася до контролния поток (той не блокира.) Асинхронното се отнася до това, когато събитието\данните се обработват (не синхронно.)
Блокиране: контролът се връща към извикване на прецесия след завършване на обработката на примитив (синхронен или асинхронен)
Без блокиране: управлението се връща към процеса веднага след извикване