Domanda

C'è un articolo / algoritmo su come posso leggere un file lungo a una certa velocità?

dico che non voglio passare 10 KB / sec mentre emissione legge.

È stato utile?

Soluzione

La soluzione grezza è solo per leggere un pezzo alla volta e poi dormire per esempio 10k poi dormire un secondo. Ma la prima domanda che mi viene da chiedere è: perché? Ci sono un paio di risposte possibili:

  1. Non si vuole creare il lavoro più velocemente di quanto si può fare; o
  2. Non si vuole creare un eccessivo carico sul sistema.

Il mio suggerimento è di non controllarlo a livello di lettura. Questo è il tipo di disordinato e impreciso. Invece controllarlo alla fine lavoro. Java ha un sacco di grandi strumenti di concorrenza per affrontare questo. Ci sono alcuni modi alternativi di fare questo.

Io tendo a come usare un produttore consumatore modello per suolatura questo tipo di problema . Ti dà grandi opzioni di essere in grado di monitorare i progressi di avere un filo di segnalazione e così via e può essere una soluzione molto pulita.

Qualcosa di simile a un ArrayBlockingQueue può essere utilizzato per il tipo di limitazione necessaria per entrambi (1) e (2). Con una capacità limitata il lettore alla fine blocco quando la coda è piena, quindi non si riempirà troppo in fretta. I lavoratori (consumatori) possono essere controllati per lavorare solo così veloce a manetta anche il tasso di copertura (2).

Altri suggerimenti

Una soluzione semplice, con la creazione di un ThrottledInputStream.

Questo dovrebbe essere usato in questo modo:

        final InputStream slowIS = new ThrottledInputStream(new BufferedInputStream(new FileInputStream("c:\\file.txt"),8000),300);

300 è il numero di kilobyte al secondo. 8000 è la dimensione del blocco per BufferedInputStream.

Questo dovrebbe ovviamente essere generalizzato implementando leggere (byte b [], int off, int len), che risparmiarvi un sacco di System.currentTimeMillis () chiama. System.currentTimeMillis () viene chiamato una volta per ciascun byte, che può causare un po 'di un overhead. Dovrebbe anche essere possibile memorizzare il numero di bytes che possono savely essere letto senza chiamare System.currentTimeMillis ().

Assicurati di mettere un BufferedInputStream in mezzo, altrimenti la FileInputStream sarà interrogato nei singoli byte, piuttosto che blocchi. Ciò consentirà di ridurre la forma il 10% a quasi 0. carico si rischia di superare la velocità dei dati per il numero di byte la dimensione del blocco.

import java.io.InputStream;
import java.io.IOException;

public class ThrottledInputStream extends InputStream {
    private final InputStream rawStream;
    private long totalBytesRead;
    private long startTimeMillis;

    private static final int BYTES_PER_KILOBYTE = 1024;
    private static final int MILLIS_PER_SECOND = 1000;
    private final int ratePerMillis;

    public ThrottledInputStream(InputStream rawStream, int kBytesPersecond) {
        this.rawStream = rawStream;
        ratePerMillis = kBytesPersecond * BYTES_PER_KILOBYTE / MILLIS_PER_SECOND;
    }

    @Override
    public int read() throws IOException {
        if (startTimeMillis == 0) {
            startTimeMillis = System.currentTimeMillis();
        }
        long now = System.currentTimeMillis();
        long interval = now - startTimeMillis;
        //see if we are too fast..
        if (interval * ratePerMillis < totalBytesRead + 1) { //+1 because we are reading 1 byte
            try {
                final long sleepTime = ratePerMillis / (totalBytesRead + 1) - interval; // will most likely only be relevant on the first few passes
                Thread.sleep(Math.max(1, sleepTime));
            } catch (InterruptedException e) {//never realized what that is good for :)
            }
        }
        totalBytesRead += 1;
        return rawStream.read();
    }
}
  • mentre! EOF
    • System.currentTimeMillis memorizzare () + 1000 (1 sec) per un lungo periodo variabile
    • leggere un buffer di 10K
    • controllare se l'ora di archiviazione è passato
      • se non è, Thread.sleep () per un tempo conservati - ora corrente

La creazione di ThrottledInputStream che prende un altro InputStream come suggerito sarebbe una bella soluzione.

Dipende un po 'se si vuol dire "non superano un certo tasso" o "stare vicino a un certo tasso."

Se vuoi dire "non superare", si può garantire che con un semplice ciclo:

 while not EOF do
    read a buffer
    Thread.wait(time)
    write the buffer
 od

La quantità di tempo di attesa è una semplice funzione della dimensione del buffer; se la dimensione del buffer è 10K byte, si vuole attendere un secondo tra legge.

Se si desidera ottenere più vicino di che, probabilmente avete bisogno di utilizzare un timer.

  • creare un Runnable per fare la lettura
  • Timer con un TimerTask per fare il lettura
  • pianificare l'TimerTask n di volte al secondo.

Se siete preoccupati per la velocità con cui si sta passando i dati a qualcos'altro, invece di controllare la lettura, inserire i dati in una struttura di dati come una coda o buffer circolare, e controllare l'altra estremità; inviare periodicamente i dati. È necessario essere attenti con questo, però, a seconda delle dimensioni set di dati e simili, perché si può incorrere in limitazioni di memoria se il lettore è molto molto più veloce di chi scrive.

Se avete utilizzato Java I / O allora si dovrebbe avere familiarità con i flussi di decorazione. Suggerisco un InputStream sottoclasse che prende un altro FileInputStream e strozza la portata. (Si potrebbe sottoclasse System.nanoTime ma questo approccio è altamente soggetto a errori e inflessibile.)

L'implementazione esatta dipenderà vostre esigenze. In genere si vuole sottolineare il momento l'ultima lettura restituito (wait). Nella lettura della corrente, dopo la lettura sottostante, <=> fino tempo sufficiente è passato per la quantità di dati trasferiti. Un più sofisticato attuazione può tamponare e tornare (quasi) subito con solo quanti più dati dettami dei tassi (attenzione che si deve restituire solo una lunghezza di lettura di 0 se il buffer è di lunghezza pari a zero).

È possibile utilizzare un RateLimiter. E rendere la propria implementazione di lettura in InputStream. Un esempio di questo può essere visto soffietto

public class InputStreamFlow extends InputStream {
    private final InputStream inputStream;
    private final RateLimiter maxBytesPerSecond;

    public InputStreamFlow(InputStream inputStream, RateLimiter limiter) {
        this.inputStream = inputStream;
        this.maxBytesPerSecond = limiter;
    }

    @Override
    public int read() throws IOException {
        maxBytesPerSecond.acquire(1);
        return (inputStream.read());
    }

    @Override
    public int read(byte[] b) throws IOException {
        maxBytesPerSecond.acquire(b.length);
        return (inputStream.read(b));
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        maxBytesPerSecond.acquire(len);
        return (inputStream.read(b,off, len));
    }
}

se si desidera limitare il flusso da 1 MB / s è possibile ottenere il flusso di input in questo modo:

final RateLimiter limiter = RateLimiter.create(RateLimiter.ONE_MB); 
final InputStreamFlow inputStreamFlow = new InputStreamFlow(originalInputStream, limiter);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top