2008-09-18 13 views
7

Estoy creando una base de datos de juguetes en C# para obtener más información sobre el compilador, el optimizador y la tecnología de indexación.Archivo asíncrono IO en .Net

Quiero mantener el máximo paralelismo entre las solicitudes (al menos de lectura) para traer páginas al grupo de búferes, pero estoy confundido sobre la mejor manera de lograr esto en .NET.

Estas son algunas de las opciones y los problemas que he encontrado con cada uno:

  1. Use System.IO.FileStream y el método

    BeginRead Pero, la posición en el archivo no es un argumento a BeginRead , es una propiedad del FileStream (establecida a través del método Seek), por lo que solo puedo emitir una solicitud a la vez y bloquear la transmisión durante ese tiempo. (¿O sí?) La documentación no está clara de lo que sucedería si mantuviera el bloqueo solo entre las llamadas Seek y BeginRead, pero lo solté antes de llamar al EndRead. ¿Alguien sabe?) Sé cómo hacer esto, no estoy seguro es la mejor manera.

  2. Parece haber otra forma, centrada en la estructura System.Threading.Overlapped y P \ Invocar a la función ReadFileEx en kernel32.dll.

    Desafortunadamente, hay pocas muestras, especialmente en idiomas administrados. Esta ruta (si se puede hacer que funcione) aparentemente también involucra el método ThreadPool.BindHandle y los hilos de terminación de E/S en el grupo de subprocesos. Me da la impresión de que esta es la forma aprobada de tratar este escenario bajo Windows, pero no lo entiendo y no puedo encontrar un punto de entrada a la documentación que sea útil para los no iniciados.

  3. ¿Algo más?

  4. En un comentario, jacob sugiere crear un nuevo FileStream por cada lectura en vuelo.

  5. Lea todo el archivo en la memoria.

    Esto funcionaría si la base de datos fuera pequeña. La base de código es pequeña, y hay muchas otras ineficiencias, pero la base de datos no lo es. También quiero asegurarme de que estoy haciendo toda la contabilidad necesaria para manejar una gran base de datos (que resulta ser una gran parte de la complejidad: paginación, clasificación externa, ...) y me preocupa que sea demasiado fácil de engañar accidentalmente.

Editar

aclaración de por qué estoy sospechoso con una solución 1: celebración de una sola cerradura hasta el final de BeginRead a EndRead significa que tengo que bloquear cualquier persona que quiera iniciar una lectura sólo porque otra lectura está en progreso. Eso se siente mal, porque el hilo que inicia la nueva lectura podría (en general) hacer un poco más de trabajo antes de que los resultados estén disponibles. (En realidad, simplemente escribir esto me ha llevado a pensar en una nueva solución, puse como una nueva respuesta.)

Respuesta

3

Lo que hicimos fue escribir una pequeña capa alrededor de los puertos de terminación de E/S, ReadFile y el estado GetQueuedCompletion en C++/CLI, y luego volver a llamar a C# cuando la operación se completó. Elegimos esta ruta sobre BeginRead y el patrón de operación cy async para proporcionar más control sobre los buffers usados ​​para leer desde el archivo (o socket). Esta fue una ganancia de rendimiento bastante grande sobre el enfoque puramente administrado que asigna nuevos bytes [] en el montón con cada lectura.

Además, hay mucho más completo en C++ ejemplos del uso de puertos IO Completion a cabo en la interwebs

+0

Esta es una buena idea. También puede evitar asignar nuevos bytes [] s (y agolpamiento del montón de objetos grandes) preasignándolos en grandes fragmentos al crear (o hacer crecer) el grupo de búferes. –

+0

Además, ahora no me refería al GetQueuedCompletionStatus (o lo leí de alguna manera), lo que probablemente explica por qué fracasaron mis intentos. Es hora de leer un poco más. –

5

No estoy seguro de ver por qué la opción 1 no funcionaría para usted. Tenga en cuenta que no puede tener dos hilos diferentes tratando de usar el mismo FileStream al mismo tiempo, ya que hacerlo definitivamente le causará problemas.BeginRead/EndRead está destinado a permitir que su código continúe ejecutándose mientras se lleva a cabo la operación IO potencialmente costosa, no para habilitar algún tipo de acceso de subprocesos múltiples a un archivo.

Así que le sugiero que busque y luego empiece.

+0

acordados; debe usar un nuevo objeto FileStream para cada lectura asincrónica en vuelo. –

1

¿Qué sucede si primero cargó el recurso (datos de archivo o lo que sea) en la memoria y luego lo compartió entre subprocesos? Dado que es un pequeño db. - No tendrás tantos problemas para tratar.

+0

Esto funciona en algunos casos, pero quise decir "pequeño" en el sentido de "pocas características" en lugar de "no mucha información". –

0

Uso enfoque # 1, pero

  1. Cuando llega una petición, tomar bloqueo A. Se usa para proteger una cola de espera de peticiones de lectura. Agrégalo a la cola y devuelve un nuevo resultado asíncrono. Si esto resulta en la primera adición a la cola, llame al paso 2 antes de regresar. Libere el bloqueo A antes de regresar.

  2. Cuando finaliza una lectura (o lo llama en el paso 1), tome el bloqueo A. Úselo para proteger la aparición de una solicitud de lectura de la cola. Tome el bloqueo B. Úselo para proteger la secuencia Seek ->BeginRead ->EndRead. Bloqueo de liberación B. Actualice el resultado de sincronización creado por el paso 1 para esta operación de lectura. (Dado que una operación de lectura completado, llamar a este nuevo.)

Esto resuelve el problema de no bloquear cualquier tema que se inicia una lectura sólo porque otra lectura está en curso, pero todavía secuencias lee modo que la corriente de la secuencia de archivo la posición no se ensucia.

Cuestiones relacionadas