multithreading - ¿Cómo elijo entre las diversas formas de hacer Threading en Delphi?
asynchronous (5)
(Lo siento, no tengo suficientes puntos para comentar, así que lo incluyo como una respuesta en lugar de otro voto para OTL)
He usado TThread, CSI y OmniThread (OTL). Las dos bibliotecas tienen curvas de aprendizaje no triviales, pero son mucho más capaces que TThread. Mi conclusión es que si vas a hacer algo significativo con el enhebrado terminarás escribiendo la mitad de la funcionalidad de la biblioteca de todos modos, así que también podrías comenzar con la versión depurada que alguien más escribió. Tanto Misha como Gabr son mejores programadores que la mayoría de nosotros, así que lo más probable es que hayan hecho un mejor trabajo que nosotros.
Miré AsyncCalls pero no hice lo suficiente de lo que quería. Una cosa que sí tiene es una función "Sincronizar" (que falta en OTL), por lo que si dependes de eso, puedes utilizar AynscCalls solo para eso. IMO al usar mensajes no es lo suficientemente difícil como para justificar la maldad de Sincronizar, así que abróchese y aprenda cómo usar los mensajes.
De los tres prefiero OTL, en gran parte debido a la colección de ejemplos, sino también porque es más autónomo. Eso es menos problemático si ya está utilizando el JCL o si trabaja en un solo lugar, pero yo hago una mezcla que incluye el trabajo por contrato y la venta de clientes al instalar el sistema de Misha es más difícil que el OTL, simplemente porque el OTL es ~ 20 archivos en un directorio Eso suena tonto, pero es importante para muchas personas.
Con OTL, la combinación de buscar los ejemplos y el código fuente para las palabras clave, y hacer preguntas en los foros funciona para mí. Estoy familiarizado con los trabajos de subprocesamiento de "tareas intensivas de CPU tradicionales", pero en este momento estoy trabajando en crear un fondo de trabajo de base de datos que tiene mucho más "bloque de subprocesos esperando DB" y menos "CPU al máximo", y el OTL está funcionando bastante bien para eso. Las principales diferencias son que puedo tener más de 30 hilos ejecutándose sin que la CPU se agote, pero detener uno es generalmente imposible.
Parece que finalmente tengo que implementar algún tipo de subprocesamiento en mi programa Delphi 2009. Si hubiera una sola forma de hacerlo, estaría fuera de funcionamiento. Pero veo varias posibilidades.
¿Alguien puede explicar cuál es la diferencia entre estos y por qué elegiría uno sobre otro?
La clase TThread en Delphi
AsyncCalls por Andreas Hausladen
OmniThreadLibrary por Primoz Gabrijelcic (gabr)
... ¿cualquier otro?
Editar:
Acabo de leer un excelente artículo de Gabr en el número de marzo de 2010 (n. ° 10) de Blaise Pascal Magazine titulado "Cuatro formas de crear un hilo". Tienes que suscribirte para ganar contenido para la revista, así que por derechos de autor, no puedo reproducir nada sustancial al respecto aquí.
En resumen, Gabr describe la diferencia entre usar TThreads, llamadas directas a la API de Windows, AsyncCalls de Andy y su propia OmniThreadLibrary. Concluye al final que:
"No estoy diciendo que tengas que elegir nada más que la forma clásica Delphi (TThread), pero aún así es bueno estar informado de las opciones que tienes"
La respuesta de Mghie es muy completa y sugiere que OmniThreadLibrary sea preferible. Pero todavía estoy interesado en las opiniones de todos acerca de cómo yo (o cualquier otra persona) debería elegir su método de enhebrado para su aplicación.
Y puedes agregar a la lista:
. 4. Llamadas directas a la API de Windows
. 5. Marco de aplicación distribuida CSI de Misha Charrett como lo sugiere LachlanG en su respuesta.
Conclusión:
Probablemente voy a ir con OmniThreadLibrary. Me gusta el trabajo de Gabr. Utilicé su Profiler GPProfile hace muchos años, y actualmente estoy usando su GPStringHash, que en realidad es parte de OTL.
Mi única preocupación podría ser actualizarlo para trabajar con procesamiento de 64 bits o Unix / Mac una vez que Embarcadero agregue esa funcionalidad a Delphi.
Existe otra biblioteca de threads Delphi menos conocida, Misha Charrett''s CSI Application Framework .
Se basa en el envío de mensajes en lugar de la memoria compartida. El mismo mecanismo de paso de mensajes se usa para comunicarse entre hilos que se ejecutan en el mismo proceso o en otros procesos, por lo que es tanto una biblioteca de subprocesos como una biblioteca de comunicación entre procesos distribuidos.
Hay un poco de una curva de aprendizaje para comenzar, pero una vez que empiezas no tienes que preocuparte por todos los problemas tradicionales de enhebrado, como los interbloqueos y la sincronización, el marco se encarga de la mayor parte de eso por ti.
Misha ha estado desarrollando esto durante años y todavía está mejorando activamente el marco y la documentación todo el tiempo. Siempre responde a las preguntas de apoyo.
Sé que este no es el método más avanzado :-) y quizás también tenga limitaciones, pero acabo de probar System. BeginThread y me pareció bastante simple, probablemente debido a la calidad de la documentación a la que me refería ... http://www.delphibasics.co.uk/RTL.asp?Name=BeginThread (IMO Neil Moffatt podría enseñar algo a MSDN o dos)
Ese es el factor más importante que encuentro al intentar aprender cosas nuevas, la calidad de la documentación, no su cantidad . Un par de horas fue todo lo que necesité, y luego volví al verdadero trabajo en lugar de preocuparme por cómo hacer que el hilo funcione.
EDITAR en realidad Rob Kennedy hace un gran trabajo al explicar BeginThread aquí BeginThread Structure - Delphi
EDITAR en realidad la forma en que Rob Kennedy explica TThread en la misma publicación, creo que cambiaré mi código para usar TThread mañana. ¡Quién sabe cómo será la próxima semana! (AsyncCalls tal vez)
TThread es una clase simple que encapsula un hilo de Windows. Usted crea una clase descendiente con un método Execute que contiene el código que debe ejecutar este subproceso, crea el subproceso y lo establece para que se ejecute y el código se ejecuta.
AsyncCalls y OmniThreadLibrary son ambas bibliotecas que crean un concepto de nivel superior sobre los hilos. Se trata de tareas , piezas de trabajo discretas que debe ejecutar de forma asincrónica. Inicia la biblioteca, configura un grupo de tareas, un grupo de hilos especiales cuyo trabajo es esperar hasta que tenga trabajo para ellos, y luego le pasa a la biblioteca un puntero de función (o puntero de método o método anónimo) que contiene el código debe ejecutarse, y se ejecuta en uno de los subprocesos del grupo de tareas y maneja muchos de los detalles de bajo nivel para usted.
No he utilizado ninguna de las dos bibliotecas, así que no puedo darte una comparación entre las dos. Pruébelos y vea qué pueden hacer, y cuál se siente mejor para usted.
Si no tiene experiencia con multi-threading, probablemente no debería comenzar con TThread
, ya que es una capa delgada sobre el threading nativo. Considero que también es un poco difícil en los bordes; no ha evolucionado mucho desde la introducción con Delphi 2, mayormente cambios para permitir la compatibilidad de Linux en el marco de tiempo de Kylix, y para corregir los defectos más obvios (como arreglar la clase MREW rota y finalmente desaprobar Suspend()
y Resume()
en la última versión de Delphi).
Usar una clase de envoltura de hilo simple básicamente también hace que el desarrollador se enfoque en un nivel que es demasiado bajo. Para hacer un uso adecuado de múltiples núcleos de CPU, es mejor centrarse en las tareas en lugar de en los subprocesos, porque la partición del trabajo con subprocesos no se adapta bien a los requisitos y entornos cambiantes; dependiendo del hardware y del otro software que se ejecute en paralelo, la cantidad óptima de los hilos pueden variar mucho, incluso en diferentes momentos en el mismo sistema. Una biblioteca a la que le pasa solo trozos de trabajo y que los programa automáticamente para hacer el mejor uso de los recursos disponibles ayuda mucho en este aspecto.
AsyncCalls es un buen primer paso para introducir hilos en una aplicación. Si tiene varias áreas en su programa donde se deben realizar varios pasos que son independientes entre sí, entonces simplemente puede ejecutarlos de forma asíncrona pasando cada uno de ellos a AsyncCalls. Incluso cuando solo tiene una de esas acciones que consumen mucho tiempo, puede ejecutarla de forma asincrónica y simplemente mostrar una UI de progreso en el hilo VCL, permitiendo opcionalmente cancelar la acción.
AsyncCalls es IMO no tan bueno para los trabajadores de fondo que permanecen durante todo el tiempo de ejecución del programa, y puede ser imposible de usar cuando algunos de los objetos en su programa tienen afinidad de hilos (como conexiones de bases de datos u objetos OLE que pueden requerir que todos las llamadas suceden en el mismo hilo).
Lo que también debes tener en cuenta es que estas acciones asincrónicas no son del tipo "dispara y olvida". Cada función AsyncCall()
sobrecargada devuelve un puntero de la interfaz IAsyncCall
que puede necesitar para mantener una referencia si desea evitar el bloqueo. Si no conserva una referencia, en el momento en que el recuento de ref llega a cero, la interfaz se liberará, lo que hará que la secuencia que libera la interfaz espere a que se complete la llamada asincrónica. Esto es algo que podría ver durante la depuración, al salir del método que creó el IAsyncCall
puede tomar una cantidad de tiempo misteriosa.
OTL es, en mi opinión, la más versátil de tus tres opciones, y la usaría sin pensarlo dos veces. Puede hacer todo lo que TThread
y AsyncCalls pueden hacer, y mucho más. Tiene un diseño de sonido, que es de alto nivel suficiente para facilitar la vida del usuario y para permitir que un puerto a un sistema Unixy (manteniendo la mayor parte de la interfaz intacta) se vea al menos posible, si no fácil. En los últimos meses también ha comenzado a adquirir algunos constructos de alto nivel para el trabajo paralelo, muy recomendado.
OTL también tiene unas pocas docenas de muestras, lo cual es importante para comenzar. AsyncCalls no tiene más que unas líneas en los comentarios, pero luego es bastante fácil de entender debido a su funcionalidad limitada (solo hace una cosa, pero lo hace bien). TThread
tiene solo una muestra, que en realidad no ha cambiado en 14 años y es principalmente un ejemplo de cómo no hacer cosas.
Cualquiera que sea la opción que elija, ninguna biblioteca eliminará la necesidad de comprender los conceptos básicos de threading. Después de leer un buen libro sobre estos es un requisito previo para cualquier codificación exitosa. El bloqueo adecuado, por ejemplo, es un requisito para todos ellos.