2010-08-05 7 views
58

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.¿Cómo elijo entre las diversas maneras de enhebrar en Delphi?

¿Alguien puede explicar cuál es la diferencia entre estos y por qué elegiría uno sobre otro.

  1. La clase TThread en Delphi

  2. AsyncCalls por Andreas Hausladen

  3. OmniThreadLibrary de Primoz Gabrijelcic (gabr)

  4. ... alguna otra?


Editar:

Acabo de leer un excelente artículo de Gabr en el marzo de 2010 (n ° 10) tema de Blaise Pascal Magazine titulado "Cuatro maneras 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. Él sí concluye al final que:

"No estoy diciendo que usted tiene que elegir otra cosa que la clásica forma de Delphi (TThread) pero aún es bueno estar informado de las opciones que tiene"

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 usted puede agregar a la lista:

. 4. Llamadas directas a la API de Windows

. 5. Misha Charrett'sCSI Distributed Application Framework según lo sugerido por 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 que funcione con el procesamiento de 64 bits o Unix/Mac una vez que Embarcadero agregue esa funcionalidad a Delphi.

Respuesta

41

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, principalmente cambios para permitir la compatibilidad de Linux en el marco de tiempo de Kylix, y para corregir los defectos más obvios (como reparar la clase MREW rota y finalmente rechazar Suspend() y Resume() en la última versión Versión Delphi).

El uso de una clase de envoltura de hilo simple básicamente también hace que el desarrollador se concentre 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 subprocesos 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 de la 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 objetos en el programa tienen afinidad de subprocesos (como conexiones de bases de datos u objetos OLE que pueden tener un requisito de que todas las llamadas sucedan en el mismo hilo).

Lo que también debe tener en cuenta es que estas acciones asincrónicas son no del tipo "ignórense y olvidarse".Cada función AsyncCall() sobrecargada devuelve un puntero de 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 sus 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 empezar. 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 no ha cambiado realmente en 14 años y es principalmente un ejemplo de cómo no hacer las cosas.

Cualquiera que sea la opción que elijas, 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.

+3

Excelente respuesta. Estoy comenzando a explorar la posibilidad de agregar subprocesos a mi aplicación que tiene algunas conversaciones ocasionalmente largas con mi base de datos y realmente quiero que deje de estar sentado allí como un orador público que ha perdido su hilo. Necesitaba solo este tipo de resumen. – jrodenhi

6

TThread es una clase simple que encapsula un subproceso 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 dos bibliotecas que crean un concepto de nivel superior sobre los subprocesos.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 usado tanto la biblioteca, así que realmente no puedo darle una comparación entre los dos. Pruébelos y vea qué pueden hacer, y cuál se siente mejor para usted.

6

Hay 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 una biblioteca de subprocesos y una biblioteca de comunicación entre procesos distribuidos.

Hay un poco de una curva de aprendizaje para empezar, pero una vez que empiece no tendrá que preocuparse por todos los problemas tradicionales de enhebrado como interbloqueos y sincronización, el marco se encarga de la mayor parte de eso.

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.

6

(lo siento, yo no tengo suficientes puntos para comentar lo que estoy poniendo esto en como una respuesta en lugar de otro voto para la comunicación cara a cara)

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.

He mirado AsyncCalls pero no hizo 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. La OMI que utiliza el envío de mensajes no es lo suficientemente difícil como para justificar la maldad de Sincronizar, así que abróchese y aprenda a 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 de 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.

1

Sé que este no es el método más avanzado :-) y tal vez tiene limitaciones también, pero acabo de probar el sistema. 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 (OMI Neil Moffatt podría enseñar a MSDN una cosa o dos)

Ese es el mayor factor de encuentro para tratar de aprender cosas nuevas, la calidad de la documentación, no es 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 realidad Rob Kennedy hace un gran trabajo explicando BeginThread aquí BeginThread Structure - Delphi

EDITAR en realidad la forma Rob Kennedy explica TThread en el mismo puesto, creo que voy a cambiar mi código para utilizar TThread Tommorrow. ¡Quién sabe cómo será la próxima semana! (AsyncCalls tal vez)

+0

Estoy completamente en desacuerdo. Es difícil imaginar algo peor para mostrarle a un principiante que llamar a las funciones de la GUI de VCL fuera del hilo principal. El código tampoco establece 'IsMultiThread'. Si uno modela su propio código después de esto, entonces el escenario está listo para bastantes errores difíciles de rastrear. – mghie

+0

@mghie, oh, te estás refiriendo al ejemplo en la página de Delphi Basics. Bueno, solo lo miré como un ejemplo de cómo iniciar un hilo por separado. No pensé que necesariamente abogara por las operaciones de GUI desde el hilo. Me alegro de haberme topado con eso. TThread, AsyncCalls o OTL me habrían tomado un par de horas más (al menos) para lograr lo que quería (que es 1: una introducción simple al multihilo, y 2: un código de trabajo para poder continuar con el resto de mi trabajo en lugar de pasar todo el día jugueteando con los hilos!). Ahora puedo aprender de los demás a mi propio ritmo, sin urgencia :-) – Sam

+0

@mghie, por cierto, había leído anteriormente que las operaciones de GUI deben estar limitadas al hilo principal, así que de hecho eso es lo que hice. Usé el hilo para llamar a un servicio web y devolver los datos al hilo principal, que luego actualizó la interfaz de usuario. Funciona muy bien, por ahora. – Sam

Cuestiones relacionadas