2009-05-16 31 views
5

में किसी निश्चित दर पर फ़ाइल पढ़ें, क्या कोई लेख/एल्गोरिदम है कि मैं एक निश्चित दर पर एक लंबी फ़ाइल कैसे पढ़ सकता हूं?जावा

कहें कि मैं पढ़ना जारी करते समय 10 केबी/सेकंड पास नहीं करना चाहता हूं।

+1

सवाल तुम क्यों चाहेगा है करने के लिए एक निश्चित दर पर एक फाइल पढ़ें? लगता है जैसे आप मांग पर डेटा पढ़ना चाहते हैं, इस प्रकार आपकी "मांग" को समझने से हम आपको बेहतर समाधान के लिए इंगित कर सकते हैं। – EFraim

+0

मैं इंटरनेट से एक बड़ी फाइल डाउनलोड करने जा रहा हूं लेकिन मैं नहीं चाहता कि मेरा एप्लिकेशन उपयोगकर्ता सेट सीमा पास करे। –

+0

https://svn.apache.org/repos/asf/hadoop/common/trunk/hadoop-tools/hadoop-distcp/src/main/java/org/apache/hadoop/tools/util/ThrottledInputStream.java – pvllnspk

उत्तर

4

कच्चे समाधान सिर्फ एक समय में एक हिस्सा पढ़ने के लिए है और फिर सो जाओ जैसे 10k फिर एक सेकंड सो जाओ। लेकिन मुझे पहले सवाल पूछना है: क्यों? कुछ संभावित उत्तर हैं:

  1. आप काम से तेज़ी से काम नहीं करना चाहते हैं; या
  2. आप सिस्टम पर बहुत अधिक भार नहीं बनाना चाहते हैं।

मेरा सुझाव इसे पढ़ने के स्तर पर नियंत्रित नहीं करना है। वह गन्दा और गलत है। इसके बजाय काम के अंत में इसे नियंत्रित करें। इस से निपटने के लिए जावा में बहुत सारे समेकन उपकरण हैं। ऐसा करने के कुछ वैकल्पिक तरीके हैं।

मुझे इस तरह की समस्या को हल करने के लिए producer consumer पैटर्न का उपयोग करना पसंद है। यह आपको रिपोर्टिंग थ्रेड करके प्रगति की निगरानी करने में सक्षम होने पर बहुत अच्छा विकल्प देता है और इसी तरह और यह वास्तव में एक साफ समाधान हो सकता है।

कुछ ArrayBlockingQueue की तरह कुछ (1) और (2) दोनों के लिए आवश्यक थ्रॉटलिंग के लिए उपयोग किया जा सकता है। सीमित क्षमता के साथ जब कतार पूरी हो जाती है तो पाठक अंततः अवरुद्ध हो जाता है, इसलिए बहुत तेज नहीं होगा। मजदूरों (उपभोक्ताओं) को केवल कवर करने के लिए नियंत्रित किया जा सकता है ताकि दर कवर (2) को भी कम किया जा सके।

1

यह इस बात पर थोड़ा निर्भर करता है कि क्या आपका मतलब है "एक निश्चित दर से अधिक न हो" या "किसी निश्चित दर के करीब रहें।"

यदि आप मतलब है "से अधिक नहीं है", आप एक सरल पाश के साथ कि गारंटी ले सकते हैं:

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

समय तक इंतजार करना की राशि बफर के आकार का एक सरल कार्य है; यदि बफर आकार 10 के बाइट्स है, तो आप पढ़ने के बीच एक सेकंड प्रतीक्षा करना चाहते हैं।

यदि आप उससे अधिक करीब जाना चाहते हैं, तो आपको शायद टाइमर का उपयोग करने की आवश्यकता है।

  • पढ़ने
  • एक TimerTask के साथ एक Timer बनाने पढ़ने
  • अनुसूची TimerTask n बार एक दूसरे करने के लिए करने के लिए एक Runnable पैदा करते हैं।

यदि आप उस गति के बारे में चिंतित हैं जिस पर आप कुछ और करने के लिए डेटा पास कर रहे हैं, तो पढ़ने को नियंत्रित करने के बजाय, डेटा को डेटा संरचना जैसे कतार या परिपत्र बफर में रखें, और उसे नियंत्रित करें दूसरा अंत; समय-समय पर डेटा भेजें। हालांकि, डेटा सेट आकार और इस तरह के आधार पर आपको सावधान रहना होगा, क्योंकि पाठक लेखक की तुलना में बहुत तेज़ है, तो आप मेमोरी सीमाओं में भाग सकते हैं।

1

यदि आपने जावा I/O का उपयोग किया है तो आपको सजावटी धाराओं से परिचित होना चाहिए। मैं एक InputStream सबक्लास का सुझाव देता हूं जो InputStream लेता है और प्रवाह दर को थ्रॉटल करता है। (आप FileInputStream उपclass कर सकते हैं लेकिन यह दृष्टिकोण अत्यधिक त्रुटि-प्रवण और लचीला है।)

आपका सटीक कार्यान्वयन आपकी सटीक आवश्यकताओं पर निर्भर करेगा। आम तौर पर आप उस समय को नोट करना चाहेंगे जब आपका अंतिम पठन लौटाया जाएगा (System.nanoTime)। वर्तमान पढ़ने पर, अंतर्निहित पढ़ने के बाद, wait जब तक स्थानांतरित डेटा की मात्रा के लिए पर्याप्त समय बीत चुका नहीं जाता है। एक अधिक परिष्कृत कार्यान्वयन बफर और वापसी (लगभग) तुरंत डेटा के अनुसार जितना अधिक डेटा हो सकता है (सावधान रहें कि यदि बफर शून्य की लंबाई है तो आपको केवल 0 की पढ़ाई लंबाई वापस करनी चाहिए)।

4
  • जबकि! EOF
    • दुकान System.currentTimeMillis() + 1000 (1 सेकंड) एक लंबे चर
    • में एक 10K बफर
    • जांच पढ़ा करता है, तो संग्रहीत समय
        बीत चुका है
      • यदि यह नहीं है, तो संग्रहीत समय के लिए Thread.sleep() - वर्तमान समय

थ्रॉटलड इनपुटस्ट्रीम बनाना जो सुझाए गए एक और इनपुटस्ट्रीम को एक अच्छा समाधान होगा।

11

थ्रॉटलड इनपुटस्ट्रीम बनाकर एक साधारण समाधान।

यह इस तरह इस्तेमाल किया जाना चाहिए:

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

300 किलोबाइट प्रति सेकंड की संख्या है। 8000 BufferedInputStream के लिए ब्लॉक आकार है।

यह निश्चित रूप से पढ़ने (बाइट बी [], int off, int len) को लागू करके सामान्यीकृत किया जाना चाहिए, जो आपको System.currentTimeMillis() कॉल का एक टन छोड़ देगा। System.currentTimeMillis() को प्रत्येक बाइट पढ़ने के लिए एक बार बुलाया जाता है, जो ओवरहेड का थोड़ा सा कारण बन सकता है। System.currentTimeMillis() को कॉल किए बिना पढ़ा जा सकता है कि बाइट्स की संख्या को स्टोर करना भी संभव होना चाहिए।

BufferedInputStream को बीच में रखना सुनिश्चित करें, अन्यथा FileInputStream को ब्लॉक के बजाए एकल बाइट्स में मतदान किया जाएगा। इससे CPU लोड फॉर्म 10% से लगभग 0 हो जाएगा। आपको ब्लॉक आकार में बाइट्स की संख्या से डेटा दर से अधिक होने का जोखिम होगा।

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(); 
    } 
} 
+1

FYI : बाधित अपवाद यह सुनिश्चित करना है कि थ्रेड तुरंत एक बाधा अनुरोध के जवाब दे, भले ही वह सो रहा हो। – Simiil

0

आप एक रेटलिमीटर का उपयोग कर सकते हैं। और इनपुटस्ट्रीम में पढ़ने का अपना कार्यान्वयन करें। इस का एक उदाहरण bellow

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)); 
    } 
} 

देखा जा सकता है अगर आप 1 एमबी से प्रवाह को सीमित करना चाहते/s आप इस तरह इनपुट स्ट्रीम प्राप्त कर सकते हैं:

final RateLimiter limiter = RateLimiter.create(RateLimiter.ONE_MB); 
final InputStreamFlow inputStreamFlow = new InputStreamFlow(originalInputStream, limiter);