2008-11-14 15 views
9

He escrito un programa que cuenta líneas, palabras y caracteres en un texto: lo hace con hilos. Funciona muy bien a veces, pero no tanto en otras ocasiones. Lo que termina sucediendo son las variables que apuntan a la cantidad de palabras y los caracteres contados a veces se quedan cortos y otras no.¿Cuándo sale un hilo de su alcance?

Me parece que los hilos a veces terminan antes de que puedan contar todas las palabras o caracteres que quieren. ¿Es porque estos subprocesos quedan fuera del alcance cuando se rompe el ciclo while (true)?

he incluido el código de la parte filiforme de mi problema a continuación:

private void countText() { 
    try { 
    reader = new BufferedReader(new FileReader("this.txt")); 
    while (true) { 
     final String line = reader.readLine(); 
     if(line == null) {break;} 
     lines++; 
     new Thread(new Runnable() {public void run() {chars += characterCounter(line);}}).start(); 
     new Thread(new Runnable() {public void run() {words += wordCounter(line);}}).start(); 
     println(line); 
    } 

    } catch(IOException ex) {return;} 

} 

(Sub Pregunta:. Esta es la primera vez que he pedido algo y código publicado no quiero para usar StackOverflow en lugar de google y wikipedia y me preocupa que esta no sea una pregunta adecuada? Traté de hacer la pregunta más general para que no solo pidiera ayuda con mi código ... pero, ¿hay alguna otra? sitio web donde este tipo de pregunta podría ser más apropiado?)

Respuesta

7

Un diseño con hebras diferente haría que sea más fácil encontrar y solucionar este tipo de problema, y ​​ser más eficiente en la negociación.Esta es una respuesta larga, pero el resumen es "si estás haciendo hilos en Java, mira java.util.concurrent tan pronto como sea humanamente posible").

Supongo que está multiprocesando este código para aprender hilos en lugar de acelerar el conteo de palabras, pero esa es una forma muy ineficiente de usar hilos. Está creando dos hilos por línea - dos mil hilos para un archivo de mil líneas. La creación de un hilo (en las JVM modernas) utiliza recursos del sistema operativo y, en general, es bastante caro. Cuando dos (y mucho menos dos mil subprocesos) tienen que acceder a un recurso compartido (como sus contadores chars y words), la contención de memoria resultante también perjudica el rendimiento.

Hacer las variables de control del synchronized como Chris Kimpton suggests o Atomic como WMR suggests probablemente arreglar el código, sino que también hará que el efecto de la contención mucho peor. Estoy bastante seguro de que irá más lento que un algoritmo de subproceso único.

Sugiero tener solo un hilo de larga duración que se ocupe de chars, y uno para words, cada uno con una cola de trabajo a la que envíe trabajos cada vez que quiera agregar un nuevo número. De esta forma, solo un hilo está escribiendo en cada variable, y si realiza cambios en el diseño, será más obvio quién es responsable de qué. También será más rápido porque no hay contención de memoria y no estás creando cientos de hilos en un círculo cerrado.

Es también importante, una vez que haya leído todas las líneas en el archivo, a espera para todos los hilos para terminar antes de imprimir los valores de los contadores, de lo contrario se pierden las actualizaciones de hilos que remanso todavía terminado. Con su diseño actual, tendría que crear una gran lista de hilos creados, y ejecutarlos al final para verificar que estén todos muertos. Con un diseño de hilo de cola y trabajador, puede indicarle a cada hilo que drene su cola y luego espere hasta que finalice.

Java (de 1.5 en adelante) hace que este tipo de diseño sea muy fácil de implementar: echa un vistazo a java.util.concurrent.Executors.newSingleThreadExecutor. También hace que sea más fácil agregar más concurrencias más adelante (suponiendo un correcto bloqueo, etc.), ya que puede cambiar a un grupo de subprocesos en lugar de un solo subproceso.

+0

No he estado esperando que los hilos terminen. Tienes razón, solo estoy haciendo esto para familiarizarte con los métodos que usaré con los hilos: la tarea no requirió hilos. ¿Cómo esperas que termine un hilo? ¿Podría esperar a que Thread.activeCount() devuelva un número pequeño? – Ziggy

+0

Thread.join() espera a que muera un solo hilo. Esperar a que el número de hilos sea igual a 1 podría funcionar: sospecho que podría encontrarse en condiciones de carrera con hilos que están en proceso de arranque, pero no estoy seguro. –

+0

Si desea conocer los hilos, le recomiendo examinar la forma de hacer ejecutables Executor/thread pool/work queue. Una vez que entiendes, en realidad es mucho más fácil razonar que crear subprocesos manualmente. –

3

Suena como una buena pregunta para mí ... Creo que el problema podría estar relacionado con el atomicit y de los caracteres + = y palabras + = - varios hilos podrían estar llamando a eso al mismo tiempo - ¿hace algo para asegurarse de que no haya entrelazado?

Eso es:

Tema 1, tiene chars = 10, quiere añadir 5

Tema 2, tiene chars = 10, quiere añadir 3

Tema 1 se resuelve nuevo total, 15

Tema 2 se resuelve nuevo total, 13

Tema 1 conjuntos de caracteres a 15

Subproceso 2 establece caracteres a 13.

Podría ser posible a menos que use sincronizado al actualizar esos valores.

+1

Aha! Verá, aprendí totalmente sobre el entrelazado y la atomicidad, sincronizado y bloqueos, pero eso nunca se me habría ocurrido. Ese es exactamente el problema, sin duda! – Ziggy

+1

Hmm ... Usé sincronizado (esto) {alrededor de + = cosas} pero aún estoy obteniendo resultados impredecibles ... – Ziggy

+0

oh hombre, no creo que sea eso. Agregué un println (Thread.activeCount()); eso me daría una idea de lo que estaba pasando. Parece que algunas veces obtengo los 12 hilos completos activos antes de que termine el ciclo while. Ese es el problema: ¡no hay suficiente tiempo! – Ziggy

4

Como Chris Kimpton ya señaló correctamente, tiene un problema con la actualización de chars y words en diferentes subprocesos. La sincronización en this tampoco funcionará porque this es una referencia al hilo actual, lo que significa que diferentes hilos se sincronizarán en diferentes objetos. Se podría utilizar un "objeto de bloqueo" extra puede sincronizar encendido pero la forma más fácil de solucionar este problema probablemente sería utilizar AtomicIntegers para los 2 contadores:

AtomicInteger chars = new AtomicInteger(); 
... 
new Thread(new Runnable() {public void run() { chars.addAndGet(characterCounter(line));}}).start(); 
... 

Si bien esto probablemente arreglar el problema, Sam Stoke's more detailed answer tiene toda la razón , el diseño original es muy ineficiente.

Para responder a su pregunta sobre cuándo sale un subproceso "fuera de alcance": está iniciando dos nuevos subprocesos para cada línea en su archivo y todos ellos se ejecutarán hasta que lleguen al final de su método run(). Esto es a menos que los haga daemon threads), en ese caso saldrán tan pronto como los hilos daemon sean los únicos que aún se estén ejecutando en esta JVM.

+0

Implementé AtomicIntegers, y eso aumentó mi tasa de éxito. Todavía hay ejecuciones en las que ambos recuentos son más bajos de lo que deberían ... – Ziggy

+0

Probablemente no espere a que se completen todos los subprocesos antes de imprimir el resultado. Ver mi respuesta a continuación. –

Cuestiones relacionadas