2011-01-17 9 views
7

que hice es crear una pequeña C++ solución que funciona muy bien trazado. Dentro de un proceso todo está bien, pero cuando abro el archivo de salida de diferentes procesos, los datos no se escriben correctamente. Abrí el archivo con FILE_SHARE_WRITE para poder escribir en el archivo cuando ya está abierto. Luego creé un mutex con nombre para asegurar la sincronización adecuada entre los procesos. Pero parece que esto no es suficiente. Según MSDN, esto funciona dentro de un proceso, pero no entre procesos diferentes. Luego intenté llamar a FlushFileBuffers después de cada escritura mientras el mutex todavía estaba retenido pero los datos todavía estaban distorsionados comoEscribir fiable a mismo archivo desde diferentes procesos

El formato es id del proceso del tiempo/método de identificación del subproceso entrar/salir/gravedad namespace + method y luego el texto del mensaje .

10:29:42.994 7448/2236  }} Dll2.Test.fndll2 L1 -> Duration: 0.094s 
10:29:43.040 7448/2236 {{  Dll2.DllMain L1 
10:29:43.134 7448/2236 Info  Dll2.DllMain L1 Process detach 
10:29:43.181 7448/2236  }} Dll2.DllMain L1 -> Duration: 0.141s 
    }} Dll2.DllMain L1 -10:29:42.681 7448/2236 Info  Dll1.DllMain L1 Process attach 
10:29:42.728 7448/2236  }} Dll1.DllMain L1 -10:29:42.744 2216/5510:29:42.775 7448/2236 {{  Dll1.Test.fndll1 10:210:29:42.822 7448/2236 Info  Dll1.Test.fndll1 10:29:42.837 2216/557610:29:42.853 7448/2236  }} Dll1.Test.fndll1 L110:29:42.884 2216/557610:29:43.306 7448/2236 {{  Dll1.DllMain L1 
10:29:43.353 7448/2236 Info  Dll1.DllMain L1 Process detach 
10:29:43.400 7448/2236  }} Dll1.DllMain L1 -> Duration: 0.094s 

He mirado FILE_FLAG_NO_BUFFERING pero tiene sus limitaciones severas y parece not easy to use.

¿Alguien sabe la manera correcta de escribir sincronizado con el mismo archivo sin distoriting la salida?

Suyo,

Alois Kraus

+0

¿Es posible implementar la solución de rastreo como un servidor COM? –

+0

@spong no solo es la forma "incorrecta", ¡ni siquiera funciona! –

+0

OK, es suficiente - comentario eliminado. – msandiford

Respuesta

0

No sé acerca de la forma "correcta", pero lo que está haciendo ya parece tan correcto como se pone a mí. La siguiente solución posible que puedo pensar sería un proceso dedicado para escribir en este archivo. El resto de los procesos se comunicaría con el proceso de registro a través de conductos nombrados y (posiblemente) exclusiones mutuas.

Tal vez incluso se puede configurará de forma que no existe un proceso explícito, pero uno de los procesos en ejecución (el que se inició por primera vez) asume esta función. Por supuesto, habría más complicaciones cuando este proceso finalice y necesite pasar la propiedad del archivo a otro proceso. En general, no es una solución muy bonita, pero debería funcionar si todo lo demás falla.

Aunque yo sospecho que todavía hay algo que ninguno de nosotros ha pensado, porque hay programas que utilizan con éxito los archivos para la comunicación.

Hmm ... pensándolo bien - que ya tiene marcas de tiempo disponible. ¿Por qué no simplemente crear una herramienta de navegación que ordene los registros por marcas de tiempo? De esa forma no importaría qué se almacena en caché.

Ah, y una tercera, aunque - I/O ha intentado mapeado en memoria? Eso está organizado de manera diferente y podría resolver su problema (sin mencionar que sería más eficiente).

+0

Para una solución de rastreo simple, un proceso adicional es demasiada sobrecarga. Usar una memoria de archivo mapeado sería una opción, pero podría ser complicado, especialmente cuando necesitas remapear el archivo y crear una ventana deslizante porque de lo contrario me comería todo el espacio de direcciones (al menos 32 bits) cuando guardo todo el archivo mapeado. –

+0

@Alois Kraus - Bueno, esa es la razón por la sugerencia de designar a uno de los procesos ya en ejecución como "maestro". Pero aquí hay algo más en lo que he pensado: ¿el puntero de "ubicación actual" en un archivo está compartido entre tus procesos? Tal vez ese es el problema. Tal vez sea local para cada proceso y debe buscar hasta el final antes de cada escritura. –

+0

La salida no se sobrescribe, pero el otro proceso se inserta en el medio de la salida de línea del otro proceso, lo que interrumpe el formateo del archivo completo. Incluso parece que es posible obtener algo P11, P12, P13, P14, P21, P22, P1P23, P24, P15,5, P16 donde la salida de un proceso se escribe parcialmente y luego se escribe una nueva salida antes que el resto del la salida anterior está escrita. Muy extraño. –

0

Como punto de partida, sugiero abandonar el modo de escritura compartida y hacer uso de su nombre mutex para administrar el acceso exclusivo al archivo. Si todo está funcionando como debería, esto debería darle salida correcta. Luego haga algunas pruebas de rendimiento para ver si puede permitirse operar de esta manera: es posible que le parezca suficiente.

2

SQLite utiliza bloqueos de archivos para garantizar que esto no ocurra con su archivo de base de datos al acceder desde múltiples procesos. ¿Has probado usando LockFile? (example). En el pasado, utilicé una base de datos SQLite para registrar desde múltiples procesos, pero eso probablemente sea demasiado en este caso.

+0

Gracias probaré esto mañana para ver si soluciona el problema. –

0

Debe proteger o sincronizar las llamadas simultáneas a la función que escribe en el archivo con un mutex con nombre. Ver CreateMutex

+0

Ya lo hago. Me gustaría saber cómo hacerlo funcionar. Un mutex global no es suficiente porque parece que cada proceso tiene su propio búfer que se vacía en el disco cuando Windows cree que debería. –

6

Finalmente lo puse en funcionamiento.El truco fue buscar al final del archivo antes de escribir mucho. De lo contrario, sobrescribiría aproximadamente la mitad de mi salida, aunque sí lo hago con un mutex de proceso cruzado antes de cada escritura.

El código es el siguiente

__int64 Seek (HANDLE hf, __int64 distance, DWORD MoveMethod) // from MSDN 
{ 
    LARGE_INTEGER li; 
    li.QuadPart = distance; 
    li.LowPart = SetFilePointer (hf, 
           li.LowPart, 
           &li.HighPart, 
           MoveMethod); 

    if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) 
    { 
     li.QuadPart = -1; 
    } 

    return li.QuadPart; 
} 



void WriteToFile(TCHAR *pData) 
{ 
    DWORD dwWritten = 0; 

    if(FALSE == ::WriteFile(_hFile, pData, _tcslen(pData)*sizeof(TCHAR), &dwWritten, NULL)) 
    { 
     _LastError = ::GetLastError(); 
     ASSERT__(FALSE); 
    } 
} 

virtual void Write(TCHAR *pStr) 
{ 
    if(_hWriteMutex != NULL) 
    { 
     DWORD res = ::WaitForSingleObject(_hWriteMutex, 120*1000); 
     if(res == WAIT_OBJECT_0 || res == WAIT_ABANDONED) // another process might have crashed while holding the mutex 
     { 
      // Ensure that we are really writing at the end of the file 
      __int64 fPos = Seek(_hFile, 0, FILE_END); 
      WriteToFile(pStr); 
      ::ReleaseMutex(_hWriteMutex); 
     } 
     else 
     { 
      ASSERT__(FALSE); 
     } 
    } 
    else 
    { 
     WriteToFile(pStr); 
    } 
} 
Cuestiones relacionadas