Una de las múltiples nuevas características incluidas en Xojo 2020r2 es la clase Worker. Un Worker proporciona una vía para que puedas ejecutar código en múltiples núcleos de la CPU.
Un Worker logra realizar esto ejecutando su código en una app de Consola. Se inician una o más apps de consola en función de los ajustes realizados sobre el Worker. Dado que estas funcionan como aplicaciones de Consola, el sistema operativo las trata como procesos independientes y los distribuye entre los núcleos disponibles de la CPU.
Nota: Por ahora, el Worker puede utilizarse con proyectos Desktop, pero tenemos previsto que también esté disponible para proyectos Web en el futuro.
Cuando funciona en modo de Depuración, los Worker utilizan Trhreads para simular aplicaciones de consola independientes, de modo que se facilite así la depuración. Esto significa que cuando se depura todo está funcionando sobre un único núcleo de la CPU. Compila tu proyecto para permitir que los Workers funcionen sobre múltiples núcleos de la CPU.
Introducción a los Worker
Añade un Worker a tus proyectos utilizando la opción Insert en la barra de herramientas o desde el menú. Esta acción creará un objeto Worker especial en tu proyecto que proporciona propiedades y eventos para controlar su funcionamiento.
Hay tres propiedades que puedes ajustar en el Inspector para un Worker.
- Core Percent: Este indica el porcentaje de los núcleos de la CPU que deben utilizarse. Por ejemplo, si tienes una CPU con 8 núcleos, entonces puedes asignar 50 a esta propiedad para limitar el Worker a un máximo de 4 núcleos de la CPU (50% sobre 8).
- Maximum Core Count: Este indica el máximo de núcleos que quieres utilizar en tu Worker. Esta propiedad funciona asociada con la anterior (Core Percent). Por ejemplo, si tienes una CPU de 12 núcleos con Core Percent definido a 50, entonces eso significará que se utilicen hasta 6 núcleos de la CPU. Si a continuación ajustas Maximum Core Count a 4, entonces el Worker no usará más de 4 (en vez de 6).
- Project Items to Include: Cuando está vacío, se incluirán todas las clases y módulos del proyecto en el proyecto de Consola del Worker para su uso. Sin embargo, puede darse el caso de que no necesites todo ello, de modo que aquí puedes seleccionar de forma concreta las clases y módulo soportados que quieres incluir (un elemento de proyecto por línea).
El objeto Worker tiene cinco eventos que puedes utilizar para proyectar los trabajos a realizar por el Worker.
- JobRequested: Cuando se inicia un Worker por primera vez, este solicita un trabajo llamando a este método en tu app. Aquí puedes devolver una String que contenga los detalles sobre el Trabajo a ejecutar por el Worker. Aquí puedes devolver información como la ruta a un archivo, una clave de una tabla de la base de datos, un URL, o cualquier otra cosa que tenga sentido. Sin embargo, has de evitar pasar grandes cantidades de datos, puesto que ralentizaría las cosas y también utilizaría más memoria. En vez de ello, permite que el Worker obtenga lo que necesite en función de la información devuelta aquí. Cuando no haya más trabajos, devuelve una cadena vacía para indicar al Worker que simplemente puede terminar.
- JobRun: El código en este evento se ejecutará en la app de Consola del Worker, donde hará uso de los múltiples núcleos de la CPU. El parámetro contiene una String con la información que ha sido devuelta por JobRequested. Recuerda que este código es independiente y no debería hacer referencia a ninguno de los valores asignados en tu app dado que no estarán disponibles en el Worker. Los únicos datos que obtiene el Worker son los enviados, de modo que pueden usarse para acceder a otros datos adicionales otras partes. Puedes devolver una String desde este evento para proporcionar información sobre el trabajo que justo se terminó de ejecutar. Además, cuando un trabajo está finalizado se solicita uno nuevo llamando a JobRequested.
- JobCompleted: Cuando JobRun finaliza, se llama al evento JobCompleted y se proporciona aquí el valor devuelto por JobRun. Puedes utilizar este método para actualizar el listado de tu trabajo o bien realizar cualquier otro tipo de seguimiento.
- JobProgressed: Puedes llamar al método SendProgress() para enviar información sobre el progreso del trabajo a tu aplicación, de modo que puedas procesarla en este evento.
- Error: Se llama al evento Error en el caso de que se haya producido un error ejecutando un trabajo. Recibirás los detalles sobre el trabajo que ha causado el error, de modo que puedas registrarlo o bien volver a añadirlo a la cola para intentar ejecutarlo de nuevo.
Una vez que hayas implementado los eventos (como mínimo JobRequested y JobRun), puedes iniciar un Worker llamando al método Start.
Los Worker se detienen automáticamente cuando no hay más trabajos para realizar o bien si pierden comunicación con la app.
Ejemplo para Contar Palabras
Hay un par de proyectos de ejemplo para el Worker incluidos con Xojo: PictureResizer y WordCounter (Examples/Worker). En particular, el ejemplo Word Counter muestra como se puede utilizar el Worker para mejorar el rendimiento mediante el uso de múltiples núcleos de la CPU. Sin usar el Worker, la tarea de contar la cantidad de palabras en cuatro archivos de texto llevaría en torno a 12 segundos. Con el Worker usando 4 núcleos de la CPU lleva en torno a 4 segundos.
El ejemplo WordCounter tiene una clase sencilla (llamada WordCounter) que cuenta las palabras de forma lenta (intencionadamente).
Hay un arra Jobs() en WordCounterWorker que contiene las rutas a los archivos de texto sobre los que se quiere contar las palabras. En el evento JobRequested, se elimina el primer item en el array y devuelto para que JobRun pueda funcionar sobre él.
JobRun usa la clase WordCounter para cargar el archivo de texto y contar sus palabras, devolviendo el conteo de palabras y la ruta del archivo sobre el que ha trabajado.
JobCompleted actualiza la ventana principal con el conteo de palabras.
Para obtener más información sobre el Worker, consulta la entrada sobre Worker en la Guía del Usuario.