2012-04-02 20 views
13

मेरे पास एक जावा प्रोग्राम है जो 20 धागे का उपयोग करता है। उनमें से प्रत्येक अपने परिणाम output.txt नामक फ़ाइल में लिखता है।थ्रेड और फ़ाइल लेखन

मुझे हमेशा output.txt में लाइनों की एक अलग संख्या मिलती है।

क्या यह धागे के सिंक्रनाइज़ेशन के साथ समस्या हो सकती है? क्या इसे संभालने का कोई तरीका है?

+0

अच्छा, यह बहुत स्पष्ट नहीं है कि आपका कार्यान्वयन कैसा है। जैसा कि मेरा सरल परीक्षण केस दिखाता है, मुझे 20 थ्रेड के साथ फ़ाइलवाइटर के साथ आउटपुट की निरंतर रेखाएं मिलती हैं। कुछ कार्यान्वयन विवरण जोड़ना आवश्यक हो सकता है। मेरा जवाब देखें – FaithReaper

उत्तर

26

क्या यह धागे के सिंक्रनाइज़ेशन की समस्या हो सकती है?

हां।

इसे संभालने का कोई तरीका है?

हां, सुनिश्चित करें कि लिखने को प्रासंगिक म्यूटेक्स पर सिंक्रनाइज़ करके क्रमबद्ध किया जाता है। या वैकल्पिक रूप से, केवल एक धागा है जो वास्तव में फ़ाइल में आउटपुट करता है, और अन्य सभी धागे बस एक कतार में लिखा जाने वाला टेक्स्ट कतार लिखते हैं जो एक लेखन धागा से खींचता है। (इस तरह 20 मुख्य धागे आई/ओ पर ब्लॉक नहीं है।)

पुन म्युटेक्स: उदाहरण के लिए, यदि वे सभी का उपयोग कर रहे एक ही FileWriter उदाहरण (या जो भी) है, जो मैं गौर करेंगे fw के रूप में करने के लिए है, तो वे इसे एक म्युटेक्स के रूप में इस्तेमाल कर सकते हैं:

synchronized (fw) { 
    fw.write(...); 
} 

वे एक अपने स्वयं के FileWriter या जो कुछ भी उपयोग कर रहे हैं, कुछ और वे सभी शेयर खोजने के म्युटेक्स किया जाना है।

लेकिन फिर, दूसरों की तरफ से I/O करने वाला धागा होने का शायद एक अच्छा तरीका भी है।

+0

क्या आप एक लेखक थ्रेड दृष्टिकोण के लिए सबसे अच्छा तरीका क्या होगा इस पर अधिक विशिष्ट हो सकते हैं? जैसे शायद एक [एकल धागा निष्पादक] का उपयोग कर (https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newSingleThreadExecutor--) और केवल उस कार्यकर्ता को कार्य सबमिट करना '? – Roland

9

मैं आपको इस तरह व्यवस्थित करने का सुझाव दूंगा: एक थ्रेड-उपभोक्ता सभी डेटा का उपभोग करेगा और इसे फ़ाइल में लिख देगा। सभी कार्यकर्ता धागे उपभोक्ता धागे को समकालिक तरीके से डेटा का उत्पादन करेंगे। या कई धागे फ़ाइल लेखन के साथ आप कुछ म्यूटेक्स या ताले कार्यान्वयन का उपयोग कर सकते हैं।

+1

+1 मैं इस सुझाव को अपने उत्तर में जोड़ रहा था क्योंकि आप अपना लिख ​​रहे थे। :-) –

1

आपको इस मामले में सिंक्रनाइज़ेशन का उपयोग करना चाहिए। कल्पना करें कि 2 धागे (टी 1 और टी 2) एक ही समय में फ़ाइल खोलें और इसे लिखना शुरू करें। पहले धागे द्वारा किए गए परिवर्तन दूसरे थ्रेड द्वारा ओवरराइट किए जाते हैं क्योंकि फ़ाइल में परिवर्तनों को सहेजने के लिए दूसरा थ्रेड अंतिम होता है। जब कोई थ्रेड टी 1 फ़ाइल में लिख रहा है, तो टी 2 को तब तक इंतजार करना चाहिए जब तक कि इसे खोलने से पहले टी 1 इसे पूरा नहीं कर लेता।

2

यदि आप प्रदर्शन की कोई समानता और प्रबंधन की आसानी चाहते हैं, तो निर्माता-उपभोक्ता कतार और केवल एक फ़ाइल-लेखक के साथ जाएं, जैसा कि एलेक्स और अन्य द्वारा सुझाया गया है। फ़ाइल पर सभी थ्रेड्स को म्यूटेक्स के साथ देने से बस गन्दा होता है - प्रत्येक डिस्क विलंब को सीधे आपके मुख्य ऐप कार्यक्षमता में स्थानांतरित किया जाता है, (अतिरिक्त विवाद के साथ)। यह धीमी नेटवर्क ड्राइव के साथ विशेष रूप से अनावश्यक है जो चेतावनी के बिना दूर जाने जाते हैं।

1

आप एक FileOutputStream रूप में अपनी फ़ाइल पकड़ कर सकते हैं तो आप इसे इस लॉक कर सकते हैं:

FileOutputStream file = ... 
.... 
// Thread safe version. 
void write(byte[] bytes) { 
    try { 
    boolean written = false; 
    do { 
     try { 
     // Lock it! 
     FileLock lock = file.getChannel().lock(); 
     try { 
      // Write the bytes. 
      file.write(bytes); 
      written = true; 
     } finally { 
      // Release the lock. 
      lock.release(); 
     } 
     } catch (OverlappingFileLockException ofle) { 
     try { 
      // Wait a bit 
      Thread.sleep(0); 
     } catch (InterruptedException ex) { 
      throw new InterruptedIOException ("Interrupted waiting for a file lock."); 
     } 
     } 
    } while (!written); 
    } catch (IOException ex) { 
    log.warn("Failed to lock " + fileName, ex); 
    } 
} 
+0

यह पूरी तरह से अनावश्यक है कि 'सिंक्रनाइज़ किया गया' मौजूद है। – EJP

+0

@EJP - देखें [फ़ाइल लॉक] (https://docs.oracle.com/javase/7/docs/api/java/nio/channels/FileLock.html) - * फ़ाइल पर रखे ताले सभी के लिए दृश्यमान होना चाहिए प्रोग्राम जिनके पास फ़ाइल तक पहुंच है, उस भाषा के बावजूद, जिसमें वे प्रोग्राम लिखे गए हैं * - इसलिए, सिद्धांत रूप में, वे * सिंक्रनाइज़ किए गए 'से बेहतर होना चाहिए लेकिन अक्सर नहीं होते हैं। – OldCurmudgeon

+0

यह गैर-अनावश्यक, या 'सिंक्रनाइज़' से बेहतर नहीं बनाता है। प्रश्न में अन्य प्रक्रियाओं या अन्य भाषाओं के बारे में कुछ भी नहीं है। – EJP

0

खैर, किसी भी कार्यान्वयन विस्तार के बिना, यह जानना कठिन है, लेकिन के रूप में अपने परीक्षण का मामला पता चलता है, मैं हमेशा मिलता है आउटपुट की 220 लाइनें, यानि, FileWriter के साथ लाइनों की निरंतर संख्या। ध्यान दें कि synchronized का उपयोग यहां किया जाता है।

import java.io.File; 
import java.io.FileWriter; 
import java.io.IOException; 
/** 
* Working example of synchonous, competitive writing to the same file. 
* @author WesternGun 
* 
*/ 
public class ThreadCompete implements Runnable { 
    private FileWriter writer; 
    private int status; 
    private int counter; 
    private boolean stop; 
    private String name; 


    public ThreadCompete(String name) { 
     this.name = name; 
     status = 0; 
     stop = false; 
     // just open the file without appending, to clear content 
     try { 
      writer = new FileWriter(new File("test.txt"), true); 
     } catch (IOException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 

    } 


    public static void main(String[] args) { 

     for (int i=0; i<20; i++) { 
      new Thread(new ThreadCompete("Thread" + i)).start(); 
     } 
    } 

    private int generateRandom(int range) { 
     return (int) (Math.random() * range); 
    } 

    @Override 
    public void run() { 
     while (!stop) { 
      try { 
       writer = new FileWriter(new File("test.txt"), true); 
       if (status == 0) { 
        writer.write(this.name + ": Begin: " + counter); 
        writer.write(System.lineSeparator()); 
        status ++; 
       } else if (status == 1) { 
        writer.write(this.name + ": Now we have " + counter + " books!"); 
        writer.write(System.lineSeparator()); 
        counter++; 
        if (counter > 8) { 
         status = 2; 
        } 

       } else if (status == 2) { 
        writer.write(this.name + ": End. " + counter); 
        writer.write(System.lineSeparator()); 
        stop = true; 
       } 
       writer.flush(); 
       writer.close(); 
      } catch (IOException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

के रूप में मैं समझता हूँ (और परीक्षण), इस प्रक्रिया में दो चरणों देखते हैं: सब बनाया है और शुरू कर दिया पूल में

  • सभी सूत्र, फ़ाइल हड़पने के लिए तैयार;
  • उनमें से एक यह पकड़ लेता है, और मैं यह तो लगता है कि आंतरिक रूप से यह ताले, अन्य सूत्र, पहुंच पाने के लिए क्योंकि मैं सामग्री है कि दो धागे से आते हैं के संयुक्त एक पंक्ति कभी नहीं रोकता है। तो जब कोई धागा लिख ​​रहा है, तो अन्य लोग प्रतीक्षा कर रहे हैं जब तक कि यह लाइन को पूरा न करे, और बहुत संभावना है, फ़ाइल को रिलीज़ करता है। तो, कोई भी दौड़ की स्थिति नहीं होगी।
  • दूसरों में से सबसे तेज़ फ़ाइल पकड़ लेता है और लिखना शुरू करता है।

ठीक है, यह सिर्फ एक भीड़ एक बाथरूम के बाहर इंतजार कर की तरह है ..... कतार बिना,

तो, अपने कार्यान्वयन अलग है, कोड दिखाने के लिए और हम इसे तोड़ने के लिए नीचे कर सकते हैं।

संबंधित मुद्दे