2012-03-19 5 views
5

Estoy programando la aplicación de subprocesos múltiples. Tengo dos hilos. Activado es para transferir algunos datos del dispositivo al búfer de datos global y el segundo es para escribir esos datos en un archivo.¿La mejor práctica sobre cómo esperar datos dentro de un hilo y escribirlos en un archivo?

Los datos del dispositivo al búfer se transfieren de forma asíncrona. El propósito del segundo hilo debe ser esperar a que se escriba la cantidad especificada de datos en el búfer de datos principal y, finalmente, escribirlo en el archivo.

Bueno, el primer hilo está en DLL y el segundo en la aplicación principal. Temporalmente resuelvo esto con eventos. El primer hilo transfiere datos desde el dispositivo al búfer principal de datos y cuenta los datos, y cuando se transfiere una cantidad específica de datos, establece un evento. El segundo espera a que se señalice el evento y, cuando lo hace, ejecuta un código para el almacén de datos. Tan simple como que está funcionando.

Thread1.Execute: 

    var DataCount, TransferedData: Integer; 

    DataCounter := 0; 
    while not Terminted do 
    begin 
    TransferData(@pData, TransferedData); 
    Inc(DataCounter, TransferedData) 
    if DataCounter >= DataCountToNotify then SetEvent(hDataCount); 
    end; 



    Thread2.Execute: 

    hndlArr[0] := hDataCount; 
    hndlArr[1] := hTerminateEvent; 

    while (not Terminated) do 
    begin 
    wRes := WaitForMultipleObjects(HandlesCount, Addr(hndlArr), false, 60000); 
    case wRes of 
     WAIT_OBJECT_0: 
     begin 
      Synchronize(WriteBuffer);     // call it from main thread 
      ResetEvent(hndlArr[0]); 
     end; 
     WAIT_OBJECT_0 + 1: 
     begin 
      ResetEvent(hTerminateEvent); 
      break; 
     end; 
     WAIT_TIMEOUT: Break; 
    end; 
    end; 

Ahora me gustaría hacer segundo hilo más independiente ... así que puedo hacer varias instancias del segundo hilo y no tener que esperar para el primer hilo. Me gustaría mover los datos que cuentan parte del código del primer subproceso al segundo, así que ya no necesitaré contar los datos en el primer subproceso. El primero será solo para fines de transferencia de datos.

Me gustaría utilizar el segundo como contador de datos y almacenar datos. Pero ahora tendré que hacer un ciclo y revisar constantemente la cantidad de datos especificada manualmente. Si tuviera while loop tendré que agregar algo de reposo para que el segundo subproceso no disminuya el rendimiento de la computadora, pero no sé por cuánto tiempo debería dormir mientras la velocidad de transferencia de datos en el primer hilo no es constante y por lo tanto la velocidad de conteo en el segundo hilo variará.

Mi conjetura que este ejemplo de código no es bueno:

Thread2.Execute: 
    var DataCount: Integer; 
    DataIdx1 := GetCurrentDataIdx; 
    while (not Terminated) do 
    begin  
    if (GetCurrentDataIdx - DataIdx1) >= DataCountToNotify then 
    begin 
     Synchronize(WriteBuffer); 
     DataIdx1 := GetCurrentIdx; 
    end; 
    sleep(???); 
    end; 

Así que mi pregunta es ¿cuál es el mejor enfoque para resolver ese problema con el recuento de datos y su almacenamiento dentro del segundo hilo? ¿Cuáles son sus experiencias y sugerencias?

+3

¿Por qué fuerza la escritura del archivo al hilo principal, 'Sincronizar (WriteBuffer)'? No entiendo cómo logrará multiplicar el segundo hilo y aún mantener el orden de escritura en un solo archivo. –

+0

El subproceso se multiplicará pero los datos se almacenarán en diferentes archivos. No escribí nada, voy a escribirlo en el mismo archivo. Mi objetivo es tener múltiples controles que puedan almacenar datos de la misma fuente en diferentes archivos. – Nix

+0

Eso es bueno saberlo, ya que la respuesta dependerá de esta información. Todavía me pregunto por qué la escritura del archivo debe aplicarse al hilo principal. –

Respuesta

5

Tiene algunos problemas. @LU RD ya ha señalado uno: no sincronice cosas que no necesiten sincronizarse. No está claro qué hace 'WriteBuffer', pero el sistema de archivos y todas las bases de datos que he utilizado están muy bien para tener un hilo que abra un archivo/tabla y escribir en ellos.

Su sistema de búfer probablemente podría prestar algo de atención. ¿Hay alguna 'cantidad de datos especificada' o es esta alguna figura teórica que permite la escritura diferida?

Normalmente, los subprocesos de productores y consumidores intercambian múltiples punteros de búfer en las colas y evitan compartir un solo búfer. Dado que se trata de una DLL y, por lo tanto, las llamadas de administración de memoria pueden ser problemáticas, probablemente también las evitaría al crear un grupo de almacenamientos intermedios en el inicio para transferir datos en todo el sistema. Usaría una clase de memoria intermedia en lugar de solo apuntadores a la memoria, pero no es absolutamente necesaria (mucho más fácil/flexible/segura).

Los bucles Sleep() son una manera espectacularmente mala de comunicarse entre hilos. El sueño() tiene sus usos, pero este no es uno de ellos. Delphi/Windows tiene muchos mecanismos de sincronización (eventos, semáforos, mutex, etc.) que hacen innecesario ese sondeo.

LU RD también ha mencionado los problemas del procesamiento paralelo de datos cuyo orden debe conservarse. Esto a menudo requiere otro hilo, una colección de estilo de lista y números de secuencia. No probaría eso hasta que las comunicaciones entre hilos funcionen bien.

+0

El código superior es solo para fines de demostración. No te molestes con Sincronizar. Ya he arreglado las cosas y uso el mecanismo adecuado para sincronizar hilos, etc. Como menciono, me gustaría contar datos y luego cuando la condición se aplica a, por ejemplo. DataCountToNotify = 8192 Me gustaría almacenar datos. Pero me gustaría evitar el sueño. ¿Hay alguna otra alternativa para hacer eso? – Nix

+0

@Nix si no me equivoco, siempre que su hilo haga "algo" en el ciclo while, entonces puede omitir el "sueño", pero puede usar sleep (1) que es un sueño de cualquier lugar entre 1 y 55 ms (Win XP tiene un problema, no estoy seguro de las versiones futuras) – ComputerSaysNo

+1

@DorinDuminica, vea [thread-sleep-is-a-sign-of-a-poorly designed-program] (https://msmvps.com/ blogs/peterritchie/archive/2007/04/26/thread-sleep-is-a-sign-of-a-poorly-designed-program.aspx) para argumentos contra el uso de 'Sleep' en un hilo. –

2

Si desea evitar la llamada Sleep() en su segundo hilo, use un temporizador que se pueda usar como TSimpleEvent.

Establezca el tiempo de suspensión para manejar todas sus condiciones de temporización. Existe la ventaja de utilizar este esquema en lugar de un Sleep() normal, ya que un temporizador que no se puede usar no dejará el hilo en un modo de suspensión profunda.

Para desechar el hilo, vea los comentarios en el código.

var 
    FEvent: TSimpleEvent; 
    FSleepTime: Integer = 100; // Short enough to handle all cases 

Constructor TThread2.Create; 
begin 
    Inherited Create(False); 
    FEvent := TSimpleEvent.Create; 
    Self.FreeOnTerminate := True; 
end; 

procedure TThread2.Execute; 
var 
    DataCount: Integer; 
begin 
    DataIdx1 := GetCurrentDataIdx; 
    while (fEvent.WaitFor(FSleepTime) = wrTimeout) do 
    begin 
    if Terminated then 
     break; 
    // Do your work 
    if (GetCurrentDataIdx - DataIdx1) >= DataCountToNotify then 
    begin 
     // Write data to buffer 
     DataIdx1 := GetCurrentIdx; 
    end; 
    end; 
end; 

// To stop the thread gracefully, call this instead of Terminate or override the DoTerminate 
procedure TThread2.SetTerminateFlag; 
begin 
    FEvent.SetEvent; 
end; 
+0

LU RD gracias, esto se ve muy prometedor. Tendré que variar el tiempo de FSleep dependiendo también de la velocidad de transferencia de datos en el primer hilo. Puedo calcular ese tiempo. Como dije, intento evitar dormir en el hilo, por eso busqué una mejor alternativa para dormir. Nunca he usado dormir en ningún hilo que programé. No lo encontré muy seguro. Me gusta que el sistema esté más determinado. Muchas gracias. Trataré de darme cuenta de la manera que sugirió. – Nix

+0

Esto es razonable a primera vista, pero no lo es. ¿Quisiste decir 'wrSignaled' en lugar de 'wrTimeout'? Además, todavía tiene la llamada Sycnhronize aquí que hace que el hilo sea peor que ineficaz, en realidad introducirá más demoras. –

+0

@MartinJames, el Synchronize es evidentemente incorrecto, pero el OP dijo que ya no era un problema. El ciclo continuará ejecutándose mientras no esté señalizado (cada milisegundo de FSleepTime). Esto se ve bien en mi mente. –

Cuestiones relacionadas