asincrónico vs no bloqueante

¿Cuál es la diferencia entre llamadas asíncronas y no bloqueadas? También entre llamadas de locking y síncronas (con ejemplos, por favor)?

    En muchas circunstancias, son nombres diferentes para la misma cosa, pero en algunos contextos son bastante diferentes. Entonces eso depende. La terminología no se aplica de manera totalmente uniforme en toda la industria del software.

    Por ejemplo, en la API de sockets clásica, un socket sin locking es uno que simplemente devuelve inmediatamente con un mensaje de error especial “would block”, mientras que un socket de locking se habría bloqueado. Tienes que usar una función separada como select o poll para saber cuándo es un buen momento para volver a intentarlo.

    Pero los sockets asíncronos (como los compatibles con los sockets de Windows) o el patrón IO asíncrono utilizado en .NET son más convenientes. Llamas a un método para iniciar una operación, y el framework te devuelve la llamada cuando está listo. Incluso aquí, hay diferencias básicas. Los sockets Asincrónicos Win32 “clasifican” sus resultados en un hilo GUI específico al pasar los mensajes de la Ventana, mientras que el IO asíncrono .NET tiene un hilo libre (no sabe a qué hilo se llamará su callback).

    Entonces, no siempre significan lo mismo. Para destilar el ejemplo de socket, podríamos decir:

    • Bloquear y sincronizar significan lo mismo: llama a la API, cuelga el hilo hasta que tiene algún tipo de respuesta y se lo devuelve.
    • No bloquear significa que si una respuesta no se puede devolver rápidamente, la API regresa inmediatamente con un error y no hace nada más. Por lo tanto, debe haber alguna forma relacionada de consultar si la API está lista para ser llamada (es decir, para simular una espera de una manera eficiente, para evitar el sondeo manual en un ciclo cerrado).
    • Asíncrono significa que la API siempre regresa inmediatamente, después de haber comenzado un esfuerzo de “fondo” para cumplir con su solicitud, por lo que debe haber alguna forma relacionada para obtener el resultado.
    • Asincrónico se refiere a algo hecho en paralelo , por ejemplo, es otro hilo.
    • El no locking a menudo se refiere al sondeo , es decir, verifica si la condición dada se cumple (el zócalo es legible, el dispositivo tiene más datos, etc.)

    Como probablemente pueda ver a partir de la multitud de respuestas diferentes (y a menudo mutuamente excluyentes), depende de a quién le pregunte. En algunos ámbitos, los términos son sinónimos. O pueden referirse a dos conceptos similares:

    • Una interpretación es que la llamada hará algo en segundo plano esencialmente sin supervisión para permitir que el progtwig no se vea frenado por un proceso prolongado que no necesita controlar. Reproducir audio podría ser un ejemplo: un progtwig podría llamar a una función para reproducir (decir) un mp3, y a partir de ese punto podría continuar con otras cosas mientras deja al sistema operativo administrar el proceso de reproducir el audio en el hardware de sonido. .
    • La interpretación alternativa es que la llamada hará algo que el progtwig necesitará monitorear, pero permitirá que la mayor parte del proceso ocurra en segundo plano y solo notificará al progtwig en puntos críticos del proceso. Por ejemplo, el archivo I asíncrono podría ser un ejemplo: el progtwig proporciona un búfer al sistema operativo para escribir en el archivo, y el sistema operativo solo notifica al progtwig cuando la operación se completa o se produce un error.

    En cualquier caso, la intención es permitir que el progtwig no se bloquee a la espera de que se complete un proceso lento: la única diferencia real es cómo se espera que responda el progtwig. ¿Qué término se refiere a qué también cambia de progtwigdor a progtwigdor, de idioma a idioma o de plataforma a plataforma? O los términos pueden referirse a conceptos completamente diferentes (como el uso de síncrono / asíncrono en relación con la progtwigción de subprocesos).

    Lo siento, pero no creo que haya una sola respuesta correcta que sea globalmente cierta.

    Poniendo esta pregunta en el contexto de NIO y NIO.2 en java 7, Async IO es un paso más avanzado que el no-locking. Con las llamadas java NIO no bloqueantes, uno configuraría todos los canales (SocketChannel, ServerSocketChannel, FileChannel, etc.) como tales llamando a AbstractSelectableChannel.configureBlocking(false) . Sin embargo, después de que esas llamadas IO regresen, es probable que aún necesite controlar las comprobaciones, tales como si leer y escribir nuevamente, etc.
    Por ejemplo,

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

    Con la API asincrónica en java 7, estos controles se pueden hacer de formas más versátiles. Una de las 2 formas es usar CompletionHandler . Tenga en cuenta que ambas llamadas de read no son bloqueantes.

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

    sincrónico / asíncrono es describir la relación entre dos módulos.
    bloquear / no bloquear es describir la situación de un módulo.

    Un ejemplo:
    Módulo X: “I”.
    Módulo Y: “librería”.
    X pregunta Y: ¿tiene un libro llamado “c ++ primer”?

    1) locking: antes de que Y responda X, X sigue esperando la respuesta. Ahora X (un módulo) está bloqueando. X e Y son dos hilos o dos procesos o un hilo o un proceso? NO lo sabemos.

    2) sin locking: antes de que Y responda X, X simplemente se va y hace otras cosas. X puede volver cada dos minutos para verificar si Y ha terminado su trabajo, o X no volverá hasta que Y lo llame. No lo sabemos Solo sabemos que X puede hacer otras cosas antes de que Y termine su trabajo. Aquí X (un módulo) no es bloqueante. X e Y son dos hilos o dos procesos o un proceso? NO lo sabemos. PERO estamos seguros de que X e Y no podrían ser un hilo.

    3) sincrónico: antes de que Y responda X, X sigue esperando la respuesta. Significa que X no puede continuar hasta que Y termine su trabajo. Ahora decimos: X e Y (dos módulos) son sincrónicos. X e Y son dos hilos o dos procesos o un hilo o un proceso? NO lo sabemos.

    4) asíncrono: antes de que Y responda X, X se va y X puede hacer otros trabajos. X no regresará hasta que Y lo llame. Ahora decimos: X e Y (dos módulos) son asincrónicos. X e Y son dos hilos o dos procesos o un proceso? NO lo sabemos. PERO estamos seguros de que X e Y no podrían ser un hilo.

    Presta atención a las dos oraciones en negrita de arriba. ¿Por qué la oración en negrita en el 2) contiene dos casos mientras que la oración en negrita en el 4) contiene solo un caso? Esta es una clave de la diferencia entre no bloqueante y asincrónico.

    Aquí hay un ejemplo típico sobre sin locking y sincrónico:

     // 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); 

    Puedes ver que este diseño no es bloqueante (se puede decir que la mayoría de las veces este ciclo hace algo sin sentido pero a los ojos de la CPU, X se está ejecutando, lo que significa que X no es bloqueante) mientras X e Y son sincrónicos porque X puede No continúes haciendo nada (X no puede saltar fuera del ciclo) hasta que reciba mensajes de Y.
    Normalmente, en este caso, hacer un locking X es mucho mejor porque el no locking gasta muchos recursos para un bucle estúpido. Pero este ejemplo es bueno para ayudarlo a comprender el hecho: no bloquear no significa asincrónico.

    Las cuatro palabras nos hacen confundir fácilmente, lo que debemos recordar es que las cuatro palabras sirven para el diseño de la architecture. Aprender a diseñar una buena architecture es la única forma de distinguirlos.

    Por ejemplo, podemos diseñar tal tipo de architecture:

     // 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("done"); // Module Y // prepare the book for X send(X, book); 

    En el ejemplo aquí, podemos decir que

    • X1 no bloquea
    • X1 y X2 son sincrónicos
    • X e Y son asincrónicos

    Si lo necesita, también puede describir esos hilos creados en X1 con las cuatro palabras.

    Las cosas más importantes son: ¿cuándo usamos síncrono en lugar de asincrónico? ¿Cuándo usamos locking en lugar de no locking?

    ¿Por qué Nginx no bloquea? ¿Por qué Apache está bloqueando?

    Para hacer una buena elección, debe analizar su necesidad y probar el rendimiento de diferentes architectures. No existe una architecture que sea adecuada para varias necesidades.

    Una llamada no bloqueante regresa inmediatamente con los datos disponibles: el número total de bytes solicitados, menos o ninguno.

    Una llamada asíncrona solicita una transferencia que se realizará en su totalidad (en su totalidad) pero se completará en algún momento futuro.

    Sin locking: esta función no esperará mientras esté en la stack.

    Asincrónico: el trabajo puede continuar en nombre de la llamada de función después de que esa llamada haya salido de la stack

    Bloqueo de llamada: el control solo se devuelve cuando finaliza la llamada.

    Llamada sin locking : el control regresa inmediatamente. Posteriormente OS de alguna manera notifica el proceso de finalización de la llamada.


    Progtwig síncrono : un progtwig que usa llamadas de locking . Para no congelarse durante la llamada, debe tener 2 o más hilos (por eso se llama Sincrónico: los hilos se ejecutan de forma síncrona).

    Progtwig asíncrono : un progtwig que usa llamadas que no bloquean . Puede tener solo 1 hilo y seguir siendo interactivo.

    Sincrónico se define como que ocurre al mismo tiempo.

    Asincrónico se define como no suceder al mismo tiempo.

    Esto es lo que causa la primera confusión. Sincrónico es en realidad lo que se conoce como paralelo. Mientras que asincrónico es secuencial, haz esto, luego haz eso.

    Ahora todo el problema se trata de modelar un comportamiento asincrónico, porque tienes alguna operación que necesita la respuesta de otra antes de que pueda comenzar. Por lo tanto, es un problema de coordinación, ¿cómo sabrá que ahora puede comenzar esa operación?

    La solución más simple se conoce como locking.

    El locking se produce cuando simplemente eliges esperar a que se haga lo otro y le devuelves una respuesta antes de pasar a la operación que lo necesitaba.

    Entonces, si necesitas poner mantequilla en una tostada, primero debes brindar por la crianza. La forma en que los coordinarías es que primero harías un brindis por la crianza, luego mirarías sin fin la tostadora hasta que saltara la tostada, y luego procederías a poner mantequilla sobre ellos.

    Es la solución más simple y funciona muy bien. No hay una razón real para no usarlo, a menos que también tenga otras cosas que necesite hacer que no requieran coordinación con las operaciones. Por ejemplo, haciendo algunos platos. ¿Por qué esperar sin hacer nada mirando la tostadora constantemente para que la tostada explote, cuando sabes que tomará un poco de tiempo, y podrías lavar un plato entero mientras termina?

    Ahí es donde entran en juego otras dos soluciones conocidas, respectivamente, como no bloqueante y asincrónica.

    No locking es cuando eliges hacer otras cosas no relacionadas mientras esperas que se realice la operación. Verificando la disponibilidad de la respuesta como mejor le parezca.

    Entonces, en lugar de mirar la tostadora para que explote. Ve y lava un plato entero. Y luego echas un vistazo a la tostadora para ver si los brindis han explotado. Si no lo han hecho, vaya a lavar otro plato, revisando la tostadora entre cada plato. Cuando vea que los brindis se han abierto, deje de lavar los platos y, en su lugar, tome la tostada y continúe con la mantequilla.

    Tener que controlar constantemente los brindis puede ser molesto, imagine que la tostadora está en otra habitación. Entre platos, pierdes el tiempo yendo a la otra habitación para ver la tostada.

    Aquí viene asincrónico.

    Asíncrono es cuando eliges hacer otras cosas no relacionadas mientras esperas que se realice la operación. Sin embargo, en lugar de verificarlo, usted delega el trabajo de verificar a otra cosa, podría ser la operación misma o un observador, y usted tiene esa cosa que lo notifica y posiblemente lo interrumpe cuando la respuesta está disponible para que pueda pasar a la otra operación que lo necesitaba.

    Es una terminología extraña. No tiene mucho sentido, ya que todas estas soluciones son formas de crear una coordinación asincrónica de tareas dependientes. Es por eso que prefiero llamarlo evented.

    Entonces, para este, decide actualizar su tostadora para que emita un pitido cuando finalicen los brindis. Usted está constantemente escuchando, incluso mientras está lavando platos. Al escuchar el pitido, usted hace una pausa en su memoria que tan pronto como termine de lavar su plato actual, se detendrá y vaya a poner la mantequilla sobre la tostada. O puede optar por interrumpir el lavado del plato actual y tratar el brindis de inmediato.

    Si tiene problemas para escuchar el pitido, puede pedirle a su compañero que mire la tostadora para usted y que le avise cuando la tostada esté lista. Su compañero puede elegir cualquiera de las tres estrategias anteriores para coordinar su tarea de mirar la tostadora y decirle cuándo están listas.

    En una nota final, es bueno entender que, si bien el no locking y la sincronización (o lo que prefiero llamar con evented) te permiten hacer otras cosas mientras esperas, no tienes demasiado. Puede elegir realizar un bucle constante para verificar el estado de una llamada sin locking, sin hacer nada más. Sin embargo, eso a menudo es peor que bloquear (como buscar la tostadora, luego alejarla y luego volver hasta que esté lista), por lo que muchas API que no bloquean te permiten pasar a un modo de locking. Para los que estén interesados, puede esperar hasta que se lo notifiquen. La desventaja en ese caso es que agregar la notificación era complejo y potencialmente costoso para empezar. Debes comprar una tostadora nueva con la función de pitido o convencer a tu pareja para que la vea por ti.

    Y una cosa más, necesita darse cuenta de las compensaciones que proporcionan los tres. Uno no es obviamente mejor que los demás. Piensa en mi ejemplo. Si tu tostadora es tan rápida, no tendrás tiempo para lavar un plato, ni siquiera comiences a lavarlo, así de rápido es tu tostadora. Comenzar con otra cosa en ese caso es solo una pérdida de tiempo y esfuerzo. El locking servirá. Del mismo modo, si lavar un plato tomará 10 veces más tiempo que el tostado. ¿Tienes que preguntarte qué es más importante que hacer? La tostada puede ponerse fría y dura en ese momento, no vale la pena, el locking también lo hará. O deberías elegir cosas más rápidas mientras esperas. Es más obvio, pero mi respuesta ya es bastante larga, mi punto es que debes pensar en todo eso, y las complejidades de implementar cada una para decidir si vale la pena, y si en realidad mejorará tu rendimiento o rendimiento.

    Editar:

    Aunque esto ya es largo, también quiero que esté completo, por lo que agregaré dos puntos más.

    1) También existe comúnmente un cuarto modelo conocido como multiplexado . Esto es cuando mientras esperas una tarea, comienzas otra, y mientras esperas ambas, comienzas una más, y así sucesivamente, hasta que hayas comenzado todas las tareas y luego, esperas inactivo, pero en todas ellos. Tan pronto como haya terminado, puede continuar con el manejo de su respuesta, y luego volver a esperar a los demás. Se lo conoce como multiplexado, porque mientras espera, necesita verificar cada tarea una tras otra para ver si están listas, ad vitam, hasta que lo haga. Es un poco una extensión además del no locking normal.

    En nuestro ejemplo, sería como iniciar la tostadora, luego el lavavajillas, luego el microondas, etc. Y luego esperar en alguno de ellos. Donde revisarías la tostadora para ver si estaba lista, de lo contrario, revisarías el lavavajillas, si no, el microondas, y otra vez.

    2) Aunque creo que es un gran error, sincrónico a menudo se usa para significar una cosa a la vez. Y asincrónico muchas cosas a la vez. Por lo tanto, verá lockings sincrónicos y no lockings para referirse a locking y no locking. Y el locking asincrónico y el no locking utilizados para referirse a multiplexados y evented.

    Realmente no entiendo cómo llegamos allí. Pero cuando se trata de IO y Computación, sincrónico y asincrónico a menudo se refieren a lo que es mejor conocido como no superpuesto y superpuesto. Es decir, asincrónico significa que IO y Computation se superponen, es decir, que suceden al mismo tiempo. Mientras que sincrónico significa que no lo son, y así ocurre de forma secuencial. Para el no-locking síncrono, eso significa que no iniciará otro IO o Computación, simplemente está ocupado esperando y simulando una llamada de locking. Desearía que la gente dejara de usar mal de manera sincrónica y asíncrona. Entonces no lo estoy alentando.

    Solo difieren en la ortografía. No hay diferencia en lo que se refieren. Para ser técnico, podrías decir que difieren en énfasis. El no locking se refiere al flujo de control (no bloquea). Asíncrono se refiere a cuando se maneja el evento \ datos (no de forma síncrona).

    Los modelos de locking requieren que la aplicación de inicio bloquee cuando se haya iniciado la E / S. Esto significa que no es posible superponer el procesamiento y la E / S al mismo tiempo. El modelo síncrono sin locking permite la superposición de procesamiento y E / S, pero requiere que la aplicación verifique el estado de la E / S de forma recurrente. Esto deja la E / S no bloqueante asíncrona, que permite la superposición de procesamiento y E / S, incluida la notificación de finalización de E / S.

    Bloqueo: el control vuelve a invocar el preceso después de que el procesamiento de primitiva (sincronización o asincronización) finaliza

    Sin locking: el control vuelve a procesarse inmediatamente después de la invocación