2010-01-15 11 views
11

Si bien leer sobre SQLite, yo tropezado con esta cita en el FAQ: "Threads are evil. Avoid them."¿Hay alguna alternativa práctica a los hilos?

Tengo un gran respeto por SQLite, así que no podía simplemente ignorar esto. Me puse a pensar qué más podría, de acuerdo con la política de "evitarlos", utilizar en su lugar para paralelizar mis tareas. Como ejemplo, la aplicación en la que estoy trabajando actualmente requiere una interfaz de usuario que siempre responda, y necesita sondear varios sitios web de vez en cuando (un proceso que toma al menos 30 segundos para cada sitio web).

Así que abrió el PDF vinculado desde que frecuentes, y, esencialmente, parece que el documento sugiere varias técnicas para aplicarse juntos con hilos, tales como barreras o memoria transaccional - en lugar de las técnicas para reemplazar las discusiones por completo.

Dado que estas técnicas no prescinden totalmente con hilos (a menos que no he entendido bien lo que dice el trabajo), puedo ver dos opciones: o bien el FAQ SQLite hace no literalmente significa lo que dice, o no existen enfoques prácticos que en realidad evita el uso de hilos por completo. ¿Hay alguno?


Sólo una pequeña nota en tasklets/programación cooperativa como alternativa - esto se ve muy bien en pequeños ejemplos, pero me pregunto si una aplicación a gran-ish IU-pesado puede ser prácticamente parallelized de una manera exclusiva cooperativa. Si lo ha hecho con éxito o sabe de tales ejemplos, esto ciertamente califica como una respuesta válida.

+1

No voy a agregar esto como una respuesta porque no tengo ganas de darles cuerpo, sino: IO no bloqueante, multitarea cooperativa, multiprocesamiento + IPC ... y quizás hilos. La combinación correcta depende de la aplicación individual. – hobbs

+0

Para el registro, las preguntas frecuentes de SQLite hacen referencia a "El problema con los hilos". A partir de su resumen: "Descartan las propiedades más esenciales y atractivas de la computación secuencial: comprensibilidad, previsibilidad y determinismo. Los hilos, como modelo de computación, son muy indeterministas, y el trabajo del programador se vuelve uno de poda que no determina. .. Defiendo el desarrollo de lenguajes de coordinación concurrentes basados ​​en formalismos sólidos y compostables. Creo que tales lenguajes producirán programas mucho más confiables y más concurrentes ". –

Respuesta

6

Nota: Esta respuesta ya no refleja con precisión lo que pienso sobre este tema. No me gusta su tono demasiado dramático y desagradable. Además, no estoy tan seguro de que la búsqueda de un software probadamente correcto haya sido tan inútil como pensé en aquel entonces. Dejo esta respuesta porque es aceptada y votada, y la edito en algo que actualmente creo que lo vandalizaría.


Finalmente llegué a leer el periódico. ¿Dónde empiezo?

El autor está cantando una canción antigua, que dice algo así: "¡Si no puedes probar que el programa es correcto, todos estamos condenados!" Suena mejor cuando se grita fuertemente acompañado de guitarras eléctricas moduladas y un ritmo de batería rápido. Los académicos comenzaron a cantar esa canción cuando la informática estaba en el dominio de las matemáticas, un mundo donde, si no tienes una prueba, no tienes nada. Incluso después de que el primer departamento de informática se escindió del departamento de matemáticas, siguieron cantando esa canción. Están cantando esa canción hoy, y nadie está escuchando. ¿Por qué? Porque el resto de nosotros estamos ocupados creando cosas útiles, cosas buenas de software que no se pueden probar como correctas.

La presencia de hilos hace que sea aún más difícil probar la corrección de un programa, pero ¿a quién le importa? Incluso sin hilos, solo los programas más triviales pueden ser probados. ¿Por qué me importa si mi programa no trivial, que no se pudo probar como correcto, es aún más difícil de probar después de usar el enhebrado? Yo no.

Si no estaba seguro de si el autor vivía en un mundo académico de ensueño, puede estar seguro de ello después de que mantiene que el lenguaje de coordinación que sugiere como alternativa a los hilos puede expresarse mejor con una "sintaxis visual" (dibujando gráficos en la pantalla). Nunca escuché esa sugerencia antes, excepto todos los años de mi carrera.Un lenguaje que solo puede ser manipulado por GUI y no juega con ninguna de las herramientas habituales del programador no es una mejora. El autor continúa citando a UML como un brillante ejemplo de una sintaxis visual que se "combina rutinariamente con C++ y Java". Rutinariamente en qué mundo?

Mientras tanto, yo y muchos otros programadores seguimos usando hilos sin demasiados problemas. El uso correcto y seguro de los hilos de rosca es casi un problema resuelto, siempre y cuando no te obsesiones con la capacidad de prueba.

Mirar. Enhebrar es un gran juguete para niños, y necesitas conocer algunas teorías y patrones de uso para usarlos bien. Al igual que con las bases de datos, el procesamiento distribuido o cualquiera de los dispositivos más allá de la escuela primaria que los programadores usan con éxito todos los días. Pero el hecho de que no puedas demostrar que es correcto no significa que esté mal.

+0

"Incluso sin hilos, solo los programas más triviales pueden ser probados correctamente", sí, solo los más triviales, como kernels y compiladores del sistema operativo. –

+0

"Incluso sin hilos, solo los programas más triviales pueden ser probados correctamente" Esto es empíricamente erróneo. –

+0

@KevinKeith Por parcial corrección, creo que tienes razón. No sabía acerca de corrección parcial cuando escribí esto; otra razón más por la cual esta es una respuesta pésima a la que nadie debería prestarle atención. –

5

La declaración en las preguntas frecuentes de SQLite, mientras la leo, es solo un comentario sobre qué tan difícil puede ser el enhebrado para los no iniciados. Es opinión del autor, y podría ser válida. Pero decir que nunca debes usar hilos es arrojar al bebé con el agua del baño, en mi opinión. Los hilos son una herramienta. Como todas las herramientas, se pueden usar y se puede abusar de ellas. Puedo leer su artículo y estar convencido de que los hilos son el diablo, pero los he usado con éxito, sin matar gatitos.

Tenga en cuenta que SQLite está escrito para ser tan ligero y fácil de entender (desde el punto de vista de la codificación) como sea posible, por lo que me imagino que roscar es una especie de antítesis de este enfoque liviano.

Además, SQLite no está destinado a ser utilizado en un entorno altamente concurrente. Si tiene uno de estos, es mejor que trabaje con una base de datos más empresarial como Postgres.

+3

-1. No creo que esta sea una interpretación razonable de la afirmación "Los hilos son malos", especialmente cuando se trata de un enlace a un documento en el que se argumenta ampliamente que los hilos son una mala idea. (Del resumen, "descartan las propiedades más esenciales y atractivas de la computación secuencial: comprensibilidad, previsibilidad y determinismo"). –

+0

¡Sin votos! Debo decir que todavía estoy en desacuerdo contigo. Los hilos Shared-Everything son como punteros C: útiles y peligrosos. La mayoría de las personas que los usan afirman que pueden ser usados ​​de manera segura, pero mi experiencia sugiere fuertemente tomar tales afirmaciones con un grano de sal. Ese documento vale la pena leerlo. –

2

Malvado, pero un mal necesario. Las abstracciones de alto nivel de los hilos (Tareas en .NET, por ejemplo) son cada vez más comunes, pero en su mayor parte, la industria no está tratando de encontrar una manera de evitar los hilos, simplemente facilitando el tratamiento de las complejidades que conlleva cualquier tipo de programación concurrente.

2

Si realmente quiere vivir sin hilos, puede, siempre y cuando no llame a ninguna función que pueda bloquear. Esto puede no ser posible.

Una alternativa es implementar las tareas que habría realizado en hilos como finite state machines. Básicamente, la tarea hace lo que puede hacer inmediatamente, luego pasa a su siguiente estado, a la espera de un evento, como la entrada que llega a un archivo o el apagado de un temporizador. X Windows, así como la mayoría de los juegos de herramientas GUI, son compatibles con este estilo. Cuando ocurre algo, llaman a una devolución de llamada, que hace lo que tiene que hacer y regresa.Para un FSM, la devolución de llamada comprueba para ver en qué estado se encuentra la tarea y cuál es el evento para determinar qué hacer inmediatamente y cuál será el siguiente estado.

Supongamos que tiene una aplicación que necesita aceptar conexiones de socket, y para cada conexión, analiza las líneas de comando, ejecuta algún código y devuelve los resultados. Una tarea sería entonces lo que escucha un socket. Cuando select() (o Gtk +, o lo que sea) le dice que el socket tiene algo que leer, lo lee en un buffer, luego verifica si tiene suficiente buffer de entrada para hacer algo. Si es así, avanza al estado de "comenzar a hacer algo", de lo contrario permanece en el estado "leyendo una línea". (Lo que "haces" podría ser estados múltiples). Cuando hayas terminado, tu tarea deja la línea del búfer y vuelve al estado "leyendo una línea". No se necesitan hilos o adelantos.

Esto le permite actuar en modo multiproceso por medio de un evento. Sin embargo, si sus máquinas de estado son complicadas, su código puede ser difícil de mantener bastante rápido, y necesitará crear algún tipo de biblioteca de administración de FSM para separar el trabajo pesado de ejecutar FSM desde el código que realmente hace las cosas. .

P.S. Otra forma de obtener hilos sin usar hilos es el GNU Pth library. No tiene preferencia, pero es otra opción si realmente no desea tratar con los hilos.

+0

¿Cómo es que el uso de Pth no usa hilos? Puede que no sean subprocesos del sistema operativo, pero todavía está compartiendo el mismo espacio de direcciones, que es el punto de evitar los subprocesos. +1 para los FSM, sin embargo. – Jacob

+0

Hay varias razones por las que alguien podría decir: "No use hilos". A veces, Pth es una alternativa aceptable, y a veces no. –

2

Una tendencia que he notado, al menos en el dominio Cocoa, es la ayuda del framework. Apple ha hecho todo lo posible para ayudar a los desarrolladores con el concepto relativamente difícil de programación simultánea. Algunas cosas que he visto:

  • Granularidad diferente de roscar. Cocoa admite todo, desde los hilos posix (bajo nivel) hasta el roscado orientado a objetos con NSLock y NSThread, hasta el parellelismo de alto nivel como NSOperation. Dependiendo de su tarea, usar una herramienta de alto nivel como NSOperation es más fácil y hace el trabajo.

  • Enlazando entre bastidores a través de una API. Muchas de las cosas de UI y animación en cacao están escondidas detrás de una API. Usted es responsable de llamar a un método API y de proporcionar una devolución de llamada asíncrona que se ejecuta cuando finaliza el hilo secundario (por ejemplo, el final de una animación).

  • openMP. Hay herramientas como openMP que le permiten proporcionar pragmas que describen al compilador que algunas tareas pueden ser configuradas de forma segura. Por ejemplo, iterar un conjunto de elementos de forma independiente.

Parece un gran impulso en esta industria es hacer cosas simples para los desarrolladores de aplicaciones y dejar los detalles morbosos de rosca a los desarrolladores de sistemas y desarrolladores de marco. Hay un impulso en la academia para formalizar patrones parellel. Como se mencionó, no se puede evitar el enhebrado, pero cada vez hay más herramientas en su arsenal para hacerlo lo más sencillo posible.

+1

"Parece que un gran impulso en esta industria es simplificar las cosas para los desarrolladores de aplicaciones y dejar los detalles del hilo sangriento a los desarrolladores de sistemas y frameworks". -- Finalmente. Cuando una biblioteca o sistema operativo hace algo, eso significa que muchos escritores de aplicaciones no tienen que hacerlo, y corren el riesgo de estropearlo. –

2

Otro enfoque para esto puede ser utilizar un modelo de simultaneidad diferente en lugar de evitar el multihilo completo (de todos modos, debe utilizar todos estos núcleos de CPU en paralelo).

Echa un vistazo a los mecanismos utilizados en Clojure (por ejemplo, agents, software transactional memory).

1

Si su tarea es muy, muy fácilmente aislable, puede utilizar los procesos en lugar de hilos, como Chrome hace por sus pestañas.

De lo contrario, dentro de un único proceso, no hay forma de lograr un paralelismo real sin hilos, porque necesita al menos dos corutinas si desea dos cosas al mismo tiempo (suponiendo que tiene múltiples procesadores/núcleos a mano, por supuesto, de lo contrario el paralelismo real simplemente no es posible).

La complejidad de enhebrar un programa siempre es relativa al grado de aislamiento de las tareas que realizarán los subprocesos. No hay problema en ejecutar varios subprocesos si está seguro de que estos nunca usarán las mismas variables. Por otra parte, existen múltiples construcciones de alto nivel en los lenguajes modernos para ayudar a sincronizar el acceso a los recursos compartidos.

Es realmente una cuestión de aplicación. Si su tarea es lo suficientemente simple como para caber en algún tipo de objeto Tarea de alto nivel (depende de su plataforma de desarrollo, su millaje puede variar), entonces usar una cola de tareas es su mejor opción. Mi regla de oro es que si no puede encontrar un nombre genérico para su hilo, entonces su tarea no es lo suficientemente importante como para justificar un hilo (en lugar de una tarea en una cola de operaciones).

2

Software Transactional Memory (STM) es un buen control de simultaneidad de concurrencia. Se adapta bien a múltiples procesadores y no tiene la mayoría de los problemas de los mecanismos convencionales de control de concurrencia. Se implementa como parte del lenguaje Haskell. Vale la pena intentarlo. Aunque, no sé cómo esto es aplicable en el contexto de SQLite.

+0

Ciertamente, pero STM no es una forma de evitar conversaciones, sino una forma de acceder de manera segura al estado compartido cuando _siempre usa hilos. –

+1

romkyns: si el documento vinculado es una indicación, las objeciones de la gente SQLite a los hilos tienen que ver con los enormes problemas con hilos * como un modelo de programación * -particularmente estilo Java, hilos compartidos-todo: la falta total de aislamiento por defecto, propensión a errores latentes, y así sucesivamente. STM no tiene esos problemas. (Tiene otros problemas.) –

2

Alternativas a las discusiones:

  • corrutinas
  • goroutines
  • MapReduce
  • WorkerPool
  • gran despacho central de la manzana + lambdas
  • OpenCL
  • Erlang

(interesante observar que la mitad de esas tecnologías se inventaron o popularizado por Google.)

Otra cosa es muchos marcos web utilizan de forma transparente múltiples hilos/procesos para el manejo de las solicitudes, y por lo general de tal manera que en su mayoría elimina los problemas asociados con el multihilo (para el usuario del framework), o al menos hace que el threading sea más bien invisible. La web es sin estado, el único estado compartido es el estado de la sesión (que no es realmente un problema ya que, por definición, una sola sesión no va a hacer cosas simultáneas) y datos en una base de datos que ya tiene su sintetizador multithreading ordenado por ti.

Es importante notar que estas son abstracciones. Las implementaciones subyacentes de estas cosas todavía usan subprocesos. Pero esto sigue siendo increíblemente útil. Del mismo modo que no usaría el ensamblador para escribir una aplicación web, no usaría hilos directamente para escribir ninguna aplicación importante. Diseñar una aplicación para usar subprocesos es demasiado complicado para que un humano pueda manejarlo.

+0

Si pudiera aceptar dos respuestas, esta sería una de las dos. No puedo decir que uno es mejor que el otro, creo que ambos son buenos y se complementan entre sí. La otra respuesta "ganó" porque tenía menos votos y, por lo tanto, muy poca visibilidad IMO. –

+0

De la misma manera que no usaría Javascript para escribir una aplicación web ... Dios espero que mi oración anterior sea completamente cierta pronto. –

1

Los subprocesos te dan la oportunidad de hacer algunas cosas malvadas, específicamente compartir estado entre diferentes rutas de ejecución.Pero ofrecen mucha comodidad; no es necesario realizar una comunicación costosa a través de los límites del proceso. Además, vienen con menos gastos generales. Así que creo que están perfectamente bien, se usan correctamente.

Creo que la clave es compartir la menor cantidad de datos posible entre los hilos; solo adhiérase a los datos de sincronización. Si intenta compartir más que eso, debe involucrarse en un código complejo que es difícil de resolver la primera vez.

+0

Ahora bien, si solo pudiera decirle a mi compilador qué código podría ejecutarse en diferentes subprocesos y evitar que comparta cualquier estado que no sea el estado explícitamente marcado como compartible ... Actualmente no hay forma de hacer cumplir su sugerencia clave más que siendo muy, muy cuidadoso (no en ningún idioma convencional que yo sepa, de todos modos) –

+0

También me gustaría que hubiera un mejor soporte de lenguaje convencional para esto. Encapsular la tarea con subprocesos como una clase que no puede ver y no expone el estado compartido ayuda, pero tiene razón en que sería mejor si se aplicara. – Jacob

0

Un método para evitar subprocesos es la multiplexación; en esencia, se crea un mecanismo liviano similar a los hilos que usted mismo maneja.

Cosa es que esto no siempre es viable. En su caso, el tiempo de sondeo de 30 s por sitio web, ¿puede dividirse en 60 piezas de 0.5 s, entre las cuales puede realizar llamadas a la interfaz de usuario? Si no, lo siento

Los hilos no son malvados, son simplemente fáciles de dispararle al pie. Si hacer Query A toma 30s y luego hacer Query B toma otros 30s, hacerlos simultáneamente en threads tomará 120s en lugar de 60 debido a la sobrecarga del hilo, peleando por el acceso al disco y varios cuellos de botella.

Pero si la Operación A consta de 5 segundos de actividad y 55 segundos de espera, mezclados aleatoriamente, y la Operación B lleva 60 segundos de trabajo real, hacerlos en hilos llevará unos 70 segundos, en comparación con el 120 cuando los ejecuta en secuencia .

La regla de oro es: los hilos deberían estar inactivos y esperar la mayor parte del tiempo. Son buenos para E/S, lentas lecturas, trabajos de baja prioridad, etc. Si desea rendimiento, use multiplexación, que requiere más trabajo pero es más rápido, más eficiente y tiene menos salvedades. (sincronizar hilos y evitar condiciones de carrera es un capítulo completamente diferente de los dolores de cabeza de hilo ...)

+0

El tiempo de sondeo consiste casi exclusivamente en la espera de que los sitios web respondan, por lo que los hilos encajan bastante bien y la multiplexación también funcionaría. –

Cuestiones relacionadas