Domanda

Sto costruendo un database di giocattoli in C# per saperne di più sulla tecnologia di compilazione, ottimizzazione e indicizzazione.

Desidero mantenere il massimo parallelismo tra le richieste (almeno di lettura) per portare le pagine nel pool di buffer, ma sono confuso su come ottenere questo risultato al meglio in .NET.

Ecco alcune opzioni e i problemi che ho riscontrato con ciascuna:

  1. Utilizzo System.IO.FileStream e il BeginRead metodo

    Ma la posizione nel file non è un argomento a favore BeginRead, è una proprietà di FileStream (impostato tramite Seek metodo), quindi posso emettere solo una richiesta alla volta e devo bloccare lo streaming per tutta la durata.(Oppure io?La documentazione non è chiara su cosa accadrebbe se tenessi il lucchetto solo tra il Seek E BeginRead chiama ma lo rilascia prima di chiamare EndRead.Qualcuno lo sa?) So come farlo, solo non sono sicuro che sia il modo migliore.

  2. Sembra che ci sia un altro modo, incentrato su System.Threading.Overlapped struttura e P\Invoke al ReadFileEx funzione in kernel32.dll.

    Sfortunatamente gli esempi scarseggiano, soprattutto nei linguaggi gestiti.Questo percorso (se può essere fatto funzionare) apparentemente coinvolge anche il ThreadPool.BindHandle metodo e i thread di completamento IO nel pool di thread.Ho l'impressione che questo sia il modo autorizzato di gestire questo scenario sotto Windows, ma non lo capisco e non riesco a trovare un punto di accesso alla documentazione che sia utile a chi non lo sapesse.

  3. Qualcos'altro?

  4. In un commento, Jacob suggerisce di crearne uno nuovo FileStream per ogni lettura in volo.

  5. Legge l'intero file in memoria.

    Funzionerebbe se il database fosse piccolo.La base di codice è piccola e ci sono molte altre inefficienze, ma il database in sé non lo è.Voglio anche essere sicuro di fare tutta la contabilità necessaria per gestire un database di grandi dimensioni (che risulta essere una parte enorme della complessità:paging, ordinamento esterno, ...) e temo che potrebbe essere troppo facile imbrogliare accidentalmente.

Modificare

Chiarimento del motivo per cui sono sospettoso con la soluzione 1:tenere un unico blocco da BeginRead a EndRead significa che devo bloccare chiunque voglia avviare una lettura solo perché è in corso un'altra lettura.Ciò sembra sbagliato, perché il thread che avvia la nuova lettura potrebbe essere in grado (in generale) di svolgere ulteriore lavoro prima che i risultati diventino disponibili.(In realtà, il solo fatto di scrivere questo mi ha portato a pensare a una nuova soluzione, che ho inserito come nuova risposta.)

È stato utile?

Soluzione

Ciò che abbiamo fatto è stato scrivere un piccolo livello attorno alle porte di completamento I/O, ReadFile e allo stato GetQueuedCompletion in C++/CLI, quindi richiamare in C# una volta completata l'operazione.Abbiamo scelto questo percorso rispetto a BeginRead e al modello di operazione asincrona C# per fornire un maggiore controllo sui buffer utilizzati per leggere dal file (o socket).Si è trattato di un notevole miglioramento delle prestazioni rispetto all'approccio puramente gestito che alloca nuovi byte[] nell'heap a ogni lettura.

Inoltre, ci sono esempi C++ molto più completi sull'utilizzo delle porte di completamento IO sugli interweb

Altri suggerimenti

Non sono sicuro di capire perché l'opzione 1 non funzionerebbe per te.Tieni presente che non puoi avere due thread diversi che tentano di utilizzare lo stesso FileStream contemporaneamente: ciò causerebbe sicuramente problemi.BeginRead/EndRead ha lo scopo di consentire al codice di continuare l'esecuzione mentre viene eseguita l'operazione di I/O potenzialmente costosa, non di abilitare una sorta di accesso multi-thread a un file.

Quindi suggerirei di cercare e poi di iniziare a leggere.

Cosa succede se caricassi prima la risorsa (dati del file o altro) in memoria e poi la condividessi tra thread?Poiché è un piccolo db.- non avrai tanti problemi da affrontare.

Utilizza l'approccio n. 1, Ma

  1. Quando arriva una richiesta, prendi il lucchetto A.Usalo per proteggere una coda di richieste di lettura in sospeso.Aggiungilo alla coda e restituisci un nuovo risultato asincrono.Se ciò comporta la prima aggiunta alla coda, richiamare il passaggio 2 prima di ritornare.Rilasciare il blocco A prima di rientrare.

  2. Quando una lettura viene completata (o richiamata dal passaggio 1), prendi il blocco A.Usalo per proteggere l'estrazione di una richiesta di lettura dalla coda.Prendi la serratura B.Usalo per proteggere il Seek -> BeginRead -> EndRead sequenza.Rilasciare il blocco B.Aggiorna il risultato asincrono creato dal passaggio 1 per questa operazione di lettura.(Dal momento che un'operazione di lettura è stata completata, richiamala di nuovo.)

Ciò risolve il problema di non bloccare alcun thread che inizia una lettura solo perché è in corso un'altra lettura, ma sequenzia comunque le letture in modo che la posizione corrente del flusso di file non venga confusa.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top