2010-08-28 15 views
6

तो यह मेरे आखिरी प्रश्न से एक निरंतरता है - तो सवाल था "एक प्रोग्राम बनाने के लिए सबसे अच्छा तरीका क्या है जो थ्रेड सुरक्षित है कि इसे फ़ाइल में डबल मान लिखने की आवश्यकता है। अगर वह कार्य बचाता है धारावाहिक के माध्यम से मूल्य एकाधिक धागे द्वारा बुलाया जा रहा है? इसे करने का सबसे अच्छा तरीका क्या है? "थ्रेड सुरक्षित StreamWriter सी # यह कैसे करें? 2

और मैंने एमएसडीएन में मिले कुछ कोड को संशोधित किया, निम्नलिखित के बारे में कैसे? यह फ़ाइल को सब कुछ सही ढंग से लिखता है।

namespace SafeThread 
{ 
    class Program 
    { 
     static void Main() 
     { 
      Threading threader = new Threading(); 

      AutoResetEvent autoEvent = new AutoResetEvent(false); 

      Thread regularThread = 
       new Thread(new ThreadStart(threader.ThreadMethod)); 
      regularThread.Start(); 

      ThreadPool.QueueUserWorkItem(new WaitCallback(threader.WorkMethod), 
       autoEvent); 

      // Wait for foreground thread to end. 
      regularThread.Join(); 

      // Wait for background thread to end. 
      autoEvent.WaitOne(); 
     } 
    } 


    class Threading 
    { 
     List<double> Values = new List<double>(); 
     static readonly Object locker = new Object(); 
     StreamWriter writer = new StreamWriter("file"); 
     static int bulkCount = 0; 
     static int bulkSize = 100000; 

     public void ThreadMethod() 
     { 
      lock (locker) 
      { 
       while (bulkCount < bulkSize) 
        Values.Add(bulkCount++); 
      } 
      bulkCount = 0; 
     } 

     public void WorkMethod(object stateInfo) 
     { 
      lock (locker) 
      { 
       foreach (double V in Values) 
       { 
        writer.WriteLine(V); 
        writer.Flush(); 
       } 
      } 
      // Signal that this thread is finished. 
      ((AutoResetEvent)stateInfo).Set(); 
     } 
    } 
} 
+4

downvotes साथ कुछ टिप्पणियां अच्छा हो गया होता। –

उत्तर

9

Thread और QueueUserWorkItem थ्रेडिंग के लिए सबसे कम उपलब्ध एपीआई हैं। मैं तब तक उनका उपयोग नहीं करता जब तक कि मैं बिल्कुल नहीं, आखिर में, कोई अन्य विकल्प नहीं था। बहुत उच्च स्तर के अमूर्तता के लिए Task कक्षा आज़माएं। विवरण के लिए, see my recent blog post on the subject

तुम भी बजाय तुल्यकालन के लिए सबसे कम उपलब्ध एपीआई के साथ हाथ से एक बनाने की कोशिश की एक उचित निर्माता/उपभोक्ता कतार के रूप में BlockingCollection<double> उपयोग कर सकते हैं।

इन पहियों को सही ढंग से पुन: पेश करना आश्चर्यजनक रूप से कठिन है। मैं इस प्रकार की आवश्यकता के लिए डिज़ाइन की गई कक्षाओं का उपयोग करने की अत्यधिक अनुशंसा करता हूं (Task और BlockingCollection, विशिष्ट होने के लिए)। वे .NET 4.0 ढांचे और are available as an add-on for .NET 3.5 में अंतर्निहित हैं।

2

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

इसके अलावा, चूंकि प्रत्येक थ्रेड अलग लॉक का उपयोग करता है, ताले का कोई मतलब नहीं है! स्ट्रीमरेटर तक पहुंचने पर आपको यह सुनिश्चित करने की ज़रूरत है कि आप एक सिंगल, साझा लॉक रखें। आपको फ्लशिंग कोड और पीढ़ी कोड के बीच लॉक की आवश्यकता नहीं है; आपको बस यह सुनिश्चित करने की ज़रूरत है कि पीढ़ी खत्म होने के बाद फ्लश चलता है।

शायद आप सही रास्ते पर हैं, हालांकि - हालांकि मैं एक सूची के बजाय एक निश्चित आकार सरणी का उपयोग करता हूं, और जब यह पूर्ण हो जाता है तो सरणी से सभी प्रविष्टियों को फ्लश करता है। यह थ्रेड लंबे समय तक रहने पर स्मृति से बाहर निकलने की संभावना से बचाता है।

+0

सूची खाली होने पर फ्लशिंग कैसे होगी? –

+1

मैंने इसे अपने उत्तर में समझाया - कतारबद्ध कार्य आइटम 'वर्कमेथ'' थ्रेड 'थ्रेड' विधि को _before_ चला सकता है। और यह भी बाद में चला सकते हैं। आप भविष्यवाणी नहीं कर सकते हैं, क्योंकि आपने यहां किसी भी तरह का स्पष्ट आदेश निर्धारित नहीं किया है। – bdonlan

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

'सही उत्तर' वास्तव में लॉकिंग/अवरुद्ध व्यवहार के मामले में आप जो खोज रहे हैं उस पर निर्भर करता है। मिसाल के तौर पर, सबसे सरल बात यह है कि इंटरमीडिएट डेटा स्ट्रक्चर को छोड़ना सिर्फ एक WriteValues ​​विधि है जैसे प्रत्येक थ्रेड 'रिपोर्टिंग' इसके परिणाम आगे बढ़ते हैं और उन्हें फ़ाइल में लिखते हैं।कुछ की तरह:

StreamWriter writer = new StreamWriter("file"); 
public void WriteValues(IEnumerable<double> values) 
{ 
    lock (writer) 
    { 
     foreach (var d in values) 
     { 
      writer.WriteLine(d); 
     } 
     writer.Flush(); 
    } 
} 
बेशक

, इसका मतलब कार्यकर्ता धागे उनके 'रिपोर्ट परिणाम' चरणों के दौरान क्रमानुसार - प्रदर्शन विशेषताओं के आधार पर, कि बस ठीक हो सकता है, हालांकि (5 मिनट उत्पन्न करने के लिए, 500ms लिखने के लिए, उदाहरण के लिए)।

स्पेक्ट्रम के दूसरे छोर पर, आप कार्यकर्ता धागे डेटा संरचना को लिखने होगा। यदि आप .NET 4 में हैं, तो मैं खुद को लॉक करने के बजाय ConcurrentQueue का उपयोग करने की सलाह दूंगा।

इसके अलावा, आप कार्यकर्ता धागे द्वारा रिपोर्ट किए जाने वाले लोगों की तुलना में बड़े बैचों में फ़ाइल I/o करना चाहते हैं, इसलिए आप कुछ आवृत्ति पर पृष्ठभूमि थ्रेड में लिखना चुन सकते हैं। स्पेक्ट्रम के उस छोर से नीचे (आप निकालना चाहते हैं Console.WriteLine वास्तविक कोड में कहता है, उन बस देखते हैं, तो आप इसे कार्रवाई में काम कर देख सकते हैं)

public class ThreadSafeFileBuffer<T> : IDisposable 
{ 
    private readonly StreamWriter m_writer; 
    private readonly ConcurrentQueue<T> m_buffer = new ConcurrentQueue<T>(); 
    private readonly Timer m_timer; 

    public ThreadSafeFileBuffer(string filePath, int flushPeriodInSeconds = 5) 
    { 
     m_writer = new StreamWriter(filePath); 
     var flushPeriod = TimeSpan.FromSeconds(flushPeriodInSeconds); 
     m_timer = new Timer(FlushBuffer, null, flushPeriod, flushPeriod); 
    } 

    public void AddResult(T result) 
    { 
     m_buffer.Enqueue(result); 
     Console.WriteLine("Buffer is up to {0} elements", m_buffer.Count); 
    } 

    public void Dispose() 
    { 
     Console.WriteLine("Turning off timer"); 
     m_timer.Dispose(); 
     Console.WriteLine("Flushing final buffer output"); 
     FlushBuffer(); // flush anything left over in the buffer 
     Console.WriteLine("Closing file"); 
     m_writer.Dispose(); 
    } 

    /// <summary> 
    /// Since this is only done by one thread at a time (almost always the background flush thread, but one time via Dispose), no need to lock 
    /// </summary> 
    /// <param name="unused"></param> 
    private void FlushBuffer(object unused = null) 
    { 
     T current; 
     while (m_buffer.TryDequeue(out current)) 
     { 
      Console.WriteLine("Buffer is down to {0} elements", m_buffer.Count); 
      m_writer.WriteLine(current); 
     } 
     m_writer.Flush(); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var tempFile = Path.GetTempFileName(); 
     using (var resultsBuffer = new ThreadSafeFileBuffer<double>(tempFile)) 
     { 
      Parallel.For(0, 100, i => 
      { 
       // simulate some 'real work' by waiting for awhile 
       var sleepTime = new Random().Next(10000); 
       Console.WriteLine("Thread {0} doing work for {1} ms", Thread.CurrentThread.ManagedThreadId, sleepTime); 
       Thread.Sleep(sleepTime); 
       resultsBuffer.AddResult(Math.PI*i); 
      }); 
     } 
     foreach (var resultLine in File.ReadAllLines(tempFile)) 
     { 
      Console.WriteLine("Line from result: {0}", resultLine); 
     } 
    } 
} 
4

तो तुम तुम कह रहे हो तरह दिखता है StreamWriter का उपयोग कर एक फ़ाइल में डेटा लिखने के लिए धागे का एक गुच्छा चाहते हैं? आसान। बस StreamWriter ऑब्जेक्ट को लॉक करें।

कोड यहाँ 5 धागे का निर्माण करेगा। प्रत्येक धागा 5 "क्रियाएं" करेगा, और प्रत्येक क्रिया के अंत में यह "फाइल" नाम की फ़ाइल में 5 लाइनें लिखेगा।

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Threading; 

namespace ConsoleApplication1 { 
    class Program { 
     static void Main() { 
      StreamWriter Writer = new StreamWriter("file"); 

      Action<int> ThreadProcedure = (i) => { 
       // A thread may perform many actions and write out the result after each action 
       // The outer loop here represents the multiple actions this thread will take 
       for (int x = 0; x < 5; x++) { 
        // Here is where the thread would generate the data for this action 
        // Well simulate work time using a call to Sleep 
        Thread.Sleep(1000); 
        // After generating the data the thread needs to lock the Writer before using it. 
        lock (Writer) { 
         // Here we'll write a few lines to the Writer 
         for (int y = 0; y < 5; y++) { 
          Writer.WriteLine("Thread id = {0}; Action id = {1}; Line id = {2}", i, x, y); 
         } 
        } 
       } 
      }; 

      //Now that we have a delegate for the thread code lets make a few instances 

      List<IAsyncResult> AsyncResultList = new List<IAsyncResult>(); 
      for (int w = 0; w < 5; w++) { 
       AsyncResultList.Add(ThreadProcedure.BeginInvoke(w, null, null)); 
      } 

      // Wait for all threads to complete 
      foreach (IAsyncResult r in AsyncResultList) { 
       r.AsyncWaitHandle.WaitOne(); 
      } 

      // Flush/Close the writer so all data goes to disk 
      Writer.Flush(); 
      Writer.Close(); 
     } 
    } 
} 

परिणाम सभी "कार्रवाई" समवर्ती प्रदर्शन किया और फाइल करने के लिए तुल्यकालिक लिखा प्रत्येक कार्य के परिणाम के साथ उस में 125 लाइनों के साथ एक फ़ाइल "फ़ाइल" होना चाहिए।

+0

आपको ऑब्जेक्ट लॉक नहीं करना चाहिए जहां आप उनके कार्यान्वयन को नियंत्रित नहीं करते हैं - क्या होगा यदि यह आंतरिक रूप से किसी अन्य धागे में खुद को लॉक करता है? - आपको ताला के रूप में उपयोग करने के लिए एक नया 'ऑब्जेक्ट' बनाना चाहिए। – bdonlan

+0

लॉक के लिए एक अलग ऑब्जेक्ट का उपयोग न करें। ऑब्जेक्ट को सीधे लॉक करना एकमात्र तरीका है यह सुनिश्चित करने का एकमात्र तरीका है कि ऑब्जेक्ट को देख सकने वाले सभी थ्रेड ऑब्जेक्ट पर एक विशेष लॉक प्राप्त कर सकते हैं। –

+0

बहुत अच्छा, बिल्कुल वही जो मैं चाहता था, वास्तव में धन्यवाद !!! –

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