Hemos leído: Multithreading Applications in Win32 (Beveridge y Wiener)

Este libro ha sido una completa decepción ya que sin ser malo –que lo es-, no me ha aportado nada nuevo. Yo esperaba descubrir esplendorosas técnicas más allá de lo habitual… pero o no las hay o los autores no se enteran mucho.

El libro ha envejecido bastante bien, ya que si obviamos los comentarios sobre Windows 3.x, 95 y NT, casi todo lo explicado continua siendo actual y útil… bugs incluídos. Eso no es óbice para que los ejemplos sean pésimos y empleen código bastante malo y desactualizado. Si bien lo último es lógico, lo primero no. Las carencias del código llevan hasta a no liberar objetos del núcleo y del GDI en algunos ejemplos… algo inaceptable dentro del código moderno (y menos aún del anterior, ese olvido terminará degradando Windows 95 hasta que nos fuerce el reinicio).

Los autores presentan una descripción somera sobre multitarea, y luego pasa a contarnos cómo usar un hilo mediante CreateThread() y cómo esperar a que éste acabe de forma incorrecta –ellos mismos lo dicen-. Tras eso nos cuentan cómo utilizar las funciones Wait… (WaitForSingleObject, WaitForMultipleObjects, …) para recibir el evento de finalización.

Luego viene un capítulo clásico que explica las secciones críticas, los mutexes, los semáforos y los eventos (éstos últimos explicados de forma bastante oscura y barroca), sin aportar nada nuevo y enfocados al tema práctico, explicando con qué funciones de Win32 se pueden hacer.

También nos cuenta cómo suspender y activar los hilos, y cómo cambiar su prioridad.

Un tema bastante interesante, pero visto casi de pasada, es la entrada/salida asíncrona. Es decir, la I/O overlapped, las APC y los completion ports. La Overlapped I/O funciona ordenando una lectura/escritura y esperando el evento correspondiente de que se ha producido (así es cómo funciona la E/S dentro del .NET y por eso un programa en C# que lea y escriba frecuentemente se ejecuta incluso más rápido que un programa hecho en C++ siempre que no use esas técnicas, que suele ser lo habitual). Las APC son algo más o menos igual, pero los completion ports son una cucada (y es una de las cosas que había leído que existían pero que no había mirado todavía); Creas un pool de hilos, los asignas mediante unas funciones y e voilà, el sistema se encarga de ir despertando/durmiendo los hilos conforme vayan haciendo falta. Todo un hándicap para la programación de servidores.

Y luego entramos en la segunda parte, los temas avanzados, como el uso de volatile y el concepto de transacción. Continúa un nuevo capítulo sobre el uso de hilos con la CRT, o más bien qué cosas hay que tener en cuenta cuando se usan hilos que llaman a funciones de C estándar.

Otro más sobre C++ y cómo C++ facilita la eliminación de errores potenciales gracias a los constructores/destructores, así como la técnica para que las funciones miembro de una clase puedan ser usadas como funciones de hilo…

Sigue con los hilos bajo MFC, la separación entre hilos con bucle de mensajes e hilos sin él, y las cosas que hay que tener en cuenta cuando un hilo maneja o accede a ventanas…

El GDI y las cosas a tener en cuenta cuando se trabaja con elementos del mismo, que no son seguros en cuanto a hilos y la demostración de que la peor técnica para controlar ventanas MDI es usar un hilo para cada ventana.

El siguiente es un poco risible. Cómo depurar programas que usen hilos; en fin, aparte de lo habitual, el autor recomienda determinación, paciencia y creatividad.

Los dos siguientes, pese a ser poco profundos, resultan interesantes: DLL e hilos dentro de ellas y comunicación entre procesos: ficheros mapeados en memoria, pipes, memoria compartida, mailslots, DDE, OLE… El problema es que cuando podría profundizar, toca todos estos temas de refilón. En fin.

El libro termina desvariando un poco, con un capítulo sobre diseño de aplicaciones, ISAPI y poco más.

Ciertamente es una obra demasiado sencilla dado lo complejo del tema y pese a ser tan monotemática…

3 comentarios sobre “Hemos leído: Multithreading Applications in Win32 (Beveridge y Wiener)”

  1. Hola Rafael.

    Ya que te acabas de leer ese libro sobre multithreading me gustaria formularte dos preguntas que me devanan los sesos habitualmente 😀

    Basicamente, entiendo operación atómica como que se bloquea el dato con el que se opera hasta que se realiza la operación completa (como una transacción). Por ejemplo, escribir/leer un registro de 64bits con un procesador de 32bits, no se puede alterar el registro hasta que el procesador haya pegado las dos pasadas.

    Y entiendo operación volátil como que se realiza la operación directamente sobre memoria RAM, sin el uso de caches intermedias.

    Ahora mis preguntas son:

    1) Una operación atómica involucra siempre volaticidad? Es decir, modificar un registro de 64bits con un procesador de 32bits asegura que siempre se realizará directamente sobre memoria RAM y no sobre una cache?

    2) Y una operación volatil involucra siempre atomicidad? Es decir, si declaro una variable de 64bits como ‘volatile’… se asegura que las operaciones sobre dicha variable serán atómicas.

    Ó … son conceptos complementarios?

    Gracias por adelantado 🙂

    Un saludo.

  2. La respuesta casi requeriría una entrada… La respuesta corta a ambos es no.

    Una variable volatil significa que el compilador se asegurará de que antes de usarla mirará en memoria su valor en lugar de usar una versión anterior situada en alguna caché como un registro o la caché del procesador (a no ser que su lugar sea la caché o un registro, en determinar ese tipo de cosas el compilador es muy bueno). Si es una variable de 64 bits, accederá en dos golpes de acceso (en teoría), imagina que entre que carga la parte baja y la alta otro hilo cambie dicha variable, por lo que el hecho de que una variable se defina como volatile no quiere decir que también su acceso se trate de una operación atómica (lo cierto es que ya podrían especificar algún tipo de palabra reservada para ello y así tener que olvidarnos de lo que sigue).

    Para eso se crean las secciones críticas. Pero para acceder a una variable tienes las funciones Interlocked (de las que hay una espuerta) en lugar de crear/entrar y salir de las mismas. Estas funciones aseguran atomicidada para el tipo de datos para el que sirve. Echa un vistazo a la sección de «interlockeds» de la MSDN: http://msdn2.microsoft.com/en-us/library/ms686360(VS.85).aspx

  3. Lo primero gracias por la respuesta 😀

    Es que la duda me venía porque no puedes declarar un Int64 como volátil. Así que siempre tengo que andar por un lado usando operaciones Interlocked y por otro lado Thread.VolatileRead() y Thread.VolatileWrite() para realizar operaciones volátiles (hablo de .NET).

    Por esta restricción, tenía la impresión de que una cosa implicaba la otra… pero cuando me apoyaba en la teoría… veía que no era así… y eso me ha hecho hasta trasnochar 😀

    Ahora queda más claro.

    Un saludo y gracias de nuevo.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *