2015-09-21 6 views
5

मैं वर्तमान में एक साधारण WPF फ़ाइल कॉपी ऐप लिख रहा हूं जो फ़ाइलों को समानांतर में प्रतिलिपि बनाता है। अब तक यह बहुत अच्छा काम करता है! यह वह सब कुछ करता है जो मैं चाहता हूं। आपरेशन के मांस को निम्न कोड ब्लॉक में है:समांतर फोरैच थ्रेड्स का ट्रैक रखें

Parallel.ForEach(Directory.GetFiles(dir).ToList(), file => 
{ 
    _destDetail.CurrOp = string.Format("Copying file: {0}", Path.GetFileName(file)); 
    File.Copy(file, file.Replace(_destDetail.Source, _destDetail.Dest), true); 
    if (_destDetail.Progress < _destDetail.MaxProgress) 
     _destDetail.Progress++; 
}); 

मैं ParallelOptions को लागू करने और साथ ही 4 धागे की अधिकतम संख्या को सीमित कर सकते हैं, लेकिन अगर वहाँ एक रास्ता सही ढंग से नज़र रखने के लिए है मैं सोच रहा था उस मामले में प्रत्येक धागा क्या करेगा?

उदाहरण के लिए, मेरे पास मेरे यूआई का कुछ हिस्सा है जो कॉपी ऑपरेशन की वर्तमान "स्थिति" को समर्पित है। मैं Grid में 4 पंक्तियां रखना चाहता हूं जिनमें प्रत्येक के पास एक विशेष धागा था और वर्तमान में यह कौन सी फाइल कॉपी कर रही थी।

मैं जानता हूँ कि मैं Interlocked उपयोग कर सकते हैं चर Parallel पाश के बाहर हैं हेरफेर करने के लिए है, लेकिन मैं कैसे Parallel लूप के अंदर से धागे की विशिष्ट चरों का ट्रैक रखने के हैं और यूआई अप करने के लिए रखने के लिए उन चर का उपयोग जिस तारीख पर थ्रेड किस फाइल पर काम कर रहा है?

+0

बस एक एफवाईआई, 'फ़ाइल.कॉपी' 'समांतर' फोरेच 'के अंदर करना एक बुरा विचार है, आपको केवल सीओएल बाध्य काम पर' समांतर 'फोरेच' का उपयोग करना चाहिए, आईओ बाध्य काम के लिए इसके शेड्यूलिंग एल्गोरिदम बहुत सारे कार्यों को शुरू करें और यदि आप एक ही थ्रेड का उपयोग करके सामान्य 'foreach' लूप में फ़ाइलों की प्रतिलिपि बना चुके हैं तो यह अधिक से अधिक समय तक पूरा होने में अधिक समय लगेगा। –

उत्तर

3

धागे को ट्रैक करने के बजाय सीधे यूआई ObserveableCollection<ProgressDetail> पर प्रगति का प्रतिनिधित्व करता है, तो आपके लूप में यह संग्रह में एक आइटम जोड़ता है जब यह शुरू होता है फिर इसे समाप्त होने पर संग्रह से हटा दें।

एक चीज़ जो आपको सावधान रहना चाहिए, वह थ्रेड सुरक्षा है, ObseveableCollection थ्रेड सुरक्षित नहीं है, इसलिए आपको केवल थ्रेड सुरक्षित तरीकों से बातचीत करनी होगी, ऐसा करने का सबसे आसान तरीका ProgressDetail ऑब्जेक्ट्स के सभी जोड़ों और निकासी को बना रहा है यूआई थ्रेड। जब आप Progress ऑब्जेक्ट बनाते हैं तो UI थ्रेड के सिंक्रनाइज़ेशन कॉन्टेक्स्ट को कैप्चर करने का अतिरिक्त लाभ भी होता है।

public ObserveableCollection<ProgressDetail> ProgressCollection {get; private set;} 

public void CopyFiles(string dir) 
{ 

    var dispatcher = Application.Current.Dispatcher; 
    Parallel.ForEach(Directory.GetFiles(dir).ToList(), file => 
    { 
     ProgressDetail progressDetail = null; 
     dispatcher.Invoke(() => 
     { 
      // We make the `Progress` object on the UI thread so it can capture the 
      // SynchronizationContext during its construction. 
      progressDetail = new ProgressDetail(file); 
      ProgressCollection.Add(progressDetail); 
     } 

     XCopy.Copy(file, file.Replace(_destDetail.Source, _destDetail.Dest), 
        true, false, progressDetail.ProgressReporter); 

     dispatcher.Invoke(() => ProgressCollection.Remove(progressDetail); 
    }); 

} 

public sealed class ProgressDetail : INotifyPropertyChanged 
{ 
    private double _progressPercentage; 

    public ProgressDetail(string fileName) 
    { 
     FileName = fileName; 
     ProgressReporter = new Progress<double>(OnProgressReported); 
    } 

    public string FileName { get; private set; } 
    public IProgress<double> ProgressReporter { get; private set; } 
    public double ProgressPercentage 
    { 
     get { return _progressPercentage; } 
     private set 
     { 
      if (value.Equals(_progressPercentage)) return; 
      _progressPercentage = value; 
      OnPropertyChanged(); 
     } 
    } 

    private void OnProgressReported(double progress) 
    { 
     ProgressPercentage = progress; 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
    private void OnPropertyChanged([CallerMemberName] string propertyName = null) 
    { 
     var temp = PropertyChanged; 
     if(temp != null) 
      temp(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

एक उदाहरण XCopy वर्ग कि प्रगति के साथ नकल होगा this answer देखें। मैं इस धारणा है कि Copy के हस्ताक्षर

public static void Copy(string source, string destination, bool overwrite, bool nobuffering, IProgress<double> handler) 

को बदल दिया गया है बना दिया है, लेकिन मैं पाठक के लिए एक व्यायाम के रूप में है कि वास्तविक परिवर्तन छोड़ दें।

अद्यतन: मैंने सार्वजनिक संपत्ति ProgressPercentage का खुलासा करने के लिए उपर्युक्त कोड उदाहरण अपडेट किया है जो उचित घटनाओं को बाध्य और बढ़ा सकता है। मैंने ईवेंट को ProgressDetail कक्षा के आंतरिक भाग में सुनकर भी स्थानांतरित कर दिया है।

+0

आप थ्रेड सुरक्षा का जिक्र करते हैं, तो आप बाध्यकारी का उल्लेख करते हैं। जब आप इसे जोड़ते/हटाते समय संग्रह को लॉक करते हैं, तो आप वास्तव में बाध्यकारी थ्रेड को सुरक्षित नहीं बना सकते हैं। – Blindy

+0

@ ब्लिंडी मैंने किसी भी लॉकिंग का उपयोग नहीं किया, मैं सभी संशोधनों को करता हूं जो UI थ्रेड पर बाध्यकारी बाध्यकारी कारण बनेंगे, जो मेरा कोड है और निकालने के लिए 'dispatcher.Invoke' का उपयोग करके करता है और [' प्रगति ' ] (https://msdn.microsoft.com/en-us/library/hh193692 (v = vs.110) .aspx) प्रगति की रिपोर्टिंग के लिए ('प्रगति 'इसे बढ़ाएगी [' प्रगति चेंज'] (https : //msdn.microsoft.com/en-us/library/hh137516 (v = vs.110) .aspx) इसके निर्माण के समय 'सिंक्रनाइज़ेशन कॉन्टेक्स्ट' का उपयोग करके, यदि वह यूआई थ्रेड था तो यह घटनाओं को बढ़ाता है यूआई धागा)। –

1

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

जैसा कि लिखा गया है _destDetail.Progress++; वास्तव में Interlocked.Increment का उपयोग करना चाहिए! (और कॉलिंग। कुर्रोप दौड़ की स्थितियों के लिए भी खुला है।)

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